Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.analysis; |
| 16 | |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 17 | import static com.google.common.collect.ImmutableList.toImmutableList; |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 18 | import static com.google.common.truth.Truth.assertThat; |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 19 | import static com.google.devtools.build.lib.packages.Attribute.attr; |
| 20 | import static com.google.devtools.build.lib.packages.BuildType.LABEL; |
| 21 | import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL; |
Googler | c5fcc86 | 2019-09-06 16:17:47 -0700 | [diff] [blame] | 22 | import static com.google.devtools.build.lib.packages.Type.STRING; |
michajlo | 660d17f | 2020-03-27 09:01:57 -0700 | [diff] [blame] | 23 | import static org.junit.Assert.assertThrows; |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 24 | |
Googler | 19226b7 | 2020-02-06 12:58:43 -0800 | [diff] [blame] | 25 | import com.google.common.collect.ImmutableMap; |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 26 | import com.google.common.collect.ImmutableSet; |
jhorvitz | 33f7648 | 2021-10-28 10:13:26 -0700 | [diff] [blame] | 27 | import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 28 | import com.google.devtools.build.lib.analysis.config.BuildOptions; |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 29 | import com.google.devtools.build.lib.analysis.config.BuildOptionsView; |
gregce | e495e6b | 2019-04-30 14:07:06 -0700 | [diff] [blame] | 30 | import com.google.devtools.build.lib.analysis.config.CoreOptions; |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 31 | import com.google.devtools.build.lib.analysis.config.FragmentOptions; |
cparsons | 21e2518 | 2019-01-15 16:00:26 -0800 | [diff] [blame] | 32 | import com.google.devtools.build.lib.analysis.config.transitions.SplitTransition; |
John Cater | 0a9e1ed | 2019-03-27 11:02:01 -0700 | [diff] [blame] | 33 | import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory; |
Florian Weikert | cca703a | 2015-12-07 09:56:38 +0000 | [diff] [blame] | 34 | import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 35 | import com.google.devtools.build.lib.analysis.util.MockRule; |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 36 | import com.google.devtools.build.lib.events.Event; |
gregce | f0a40ac | 2020-03-31 14:11:30 -0700 | [diff] [blame] | 37 | import com.google.devtools.build.lib.events.EventHandler; |
janakr | a56a6ad | 2018-02-02 15:52:22 -0800 | [diff] [blame] | 38 | import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault; |
John Cater | 2c0dece | 2019-04-02 09:18:18 -0700 | [diff] [blame] | 39 | import com.google.devtools.build.lib.packages.AttributeTransitionData; |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 40 | import com.google.devtools.build.lib.packages.NoSuchTargetException; |
| 41 | import com.google.devtools.build.lib.packages.Package; |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 42 | import com.google.devtools.build.lib.testutil.TestRuleClassProvider; |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 43 | import java.util.Map; |
John Cater | 660e8a5 | 2021-04-20 08:00:56 -0700 | [diff] [blame] | 44 | import java.util.regex.Pattern; |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 45 | import org.junit.Test; |
| 46 | import org.junit.runner.RunWith; |
| 47 | import org.junit.runners.JUnit4; |
| 48 | |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 49 | /** Tests that check that dependency cycles are reported correctly. */ |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 50 | @RunWith(JUnit4.class) |
Florian Weikert | cca703a | 2015-12-07 09:56:38 +0000 | [diff] [blame] | 51 | public class CircularDependencyTest extends BuildViewTestCase { |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 52 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 53 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 54 | public void testOneRuleCycle() throws Exception { |
| 55 | checkError( |
| 56 | "cycle", |
| 57 | "foo.g", |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 58 | // error message |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 59 | selfEdgeMsg("//cycle:foo.g"), |
| 60 | // Rule |
| 61 | "genrule(name = 'foo.g',", |
| 62 | " outs = ['Foo.java'],", |
| 63 | " srcs = ['foo.g'],", |
| 64 | " cmd = 'cat $(SRCS) > $<' )"); |
| 65 | } |
| 66 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 67 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 68 | public void testDirectPackageGroupCycle() throws Exception { |
| 69 | checkError( |
| 70 | "cycle", |
| 71 | "melon", |
| 72 | selfEdgeMsg("//cycle:moebius"), |
| 73 | "package_group(name='moebius', packages=[], includes=['//cycle:moebius'])", |
| 74 | "sh_library(name='melon', visibility=[':moebius'])"); |
| 75 | } |
| 76 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 77 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 78 | public void testThreeLongPackageGroupCycle() throws Exception { |
John Cater | 660e8a5 | 2021-04-20 08:00:56 -0700 | [diff] [blame] | 79 | @SuppressWarnings("ConstantPatternCompile") |
| 80 | Pattern expectedEvent = |
| 81 | Pattern.compile( |
| 82 | "cycle in dependency graph:\n" |
| 83 | + " //cycle:superman \\([a-f0-9]+\\)\n" |
| 84 | + ".-> //cycle:rock \\(null\\)\n" |
| 85 | + "| //cycle:paper \\(null\\)\n" |
| 86 | + "| //cycle:scissors \\(null\\)\n" |
| 87 | + "`-- //cycle:rock \\(null\\)"); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 88 | checkError( |
| 89 | "cycle", |
| 90 | "superman", |
| 91 | expectedEvent, |
| 92 | "# dummy line", |
| 93 | "package_group(name='paper', includes=['//cycle:scissors'])", |
| 94 | "package_group(name='rock', includes=['//cycle:paper'])", |
| 95 | "package_group(name='scissors', includes=['//cycle:rock'])", |
| 96 | "sh_library(name='superman', visibility=[':rock'])"); |
| 97 | |
John Cater | 660e8a5 | 2021-04-20 08:00:56 -0700 | [diff] [blame] | 98 | Event foundEvent = assertContainsEvent(expectedEvent); |
adonovan | 07b15e6 | 2020-04-09 18:32:33 -0700 | [diff] [blame] | 99 | assertThat(foundEvent.getLocation().toString()).isEqualTo("/workspace/cycle/BUILD:3:14"); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 100 | } |
| 101 | |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 102 | /** Test to detect implicit input/output file overlap in rules. */ |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 103 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 104 | public void testOneRuleImplicitCycleJava() throws Exception { |
| 105 | Package pkg = |
| 106 | createScratchPackageForImplicitCycle( |
Googler | 83830c2 | 2024-10-05 08:45:04 -0700 | [diff] [blame] | 107 | "cycle", |
| 108 | "load('@rules_java//java:defs.bzl', 'java_library')", |
| 109 | "java_library(name='jcyc',", |
| 110 | " srcs = ['libjcyc.jar', 'foo.java'])"); |
jcater | 42edea6 | 2019-05-01 08:46:18 -0700 | [diff] [blame] | 111 | assertThrows(NoSuchTargetException.class, () -> pkg.getTarget("jcyc")); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 112 | assertThat(pkg.containsErrors()).isTrue(); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 113 | assertContainsEvent("rule 'jcyc' has file 'libjcyc.jar' as both an" + " input and an output"); |
| 114 | } |
| 115 | |
| 116 | /** |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 117 | * Test not to detect implicit input/output file overlap in rules, when coming from a different |
| 118 | * package. |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 119 | */ |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 120 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 121 | public void testInputOutputConflictDifferentPackage() throws Exception { |
| 122 | Package pkg = |
| 123 | createScratchPackageForImplicitCycle( |
| 124 | "googledata/xxx", |
| 125 | "genrule(name='geo',", |
| 126 | " srcs = ['//googledata/geo:geo_info.txt'],", |
| 127 | " outs = ['geoinfo.txt'],", |
| 128 | " cmd = '$(SRCS) > $@')"); |
lberki | aea56b3 | 2017-05-30 12:35:33 +0200 | [diff] [blame] | 129 | assertThat(pkg.containsErrors()).isFalse(); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 130 | } |
| 131 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 132 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 133 | public void testTwoRuleCycle() throws Exception { |
| 134 | scratchRule("b", "rule2", "cc_library(name='rule2',", " deps=['//a:rule1'])"); |
| 135 | |
| 136 | checkError( |
| 137 | "a", |
| 138 | "rule1", |
John Cater | 660e8a5 | 2021-04-20 08:00:56 -0700 | [diff] [blame] | 139 | Pattern.compile( |
| 140 | "in cc_library rule //a:rule1: cycle in dependency graph:\n" |
| 141 | + ".-> //a:rule1 \\([a-f0-9]+\\)\n" |
| 142 | + "| //b:rule2 \\([a-f0-9]+\\)\n" |
| 143 | + "`-- //a:rule1 \\([a-f0-9]+\\)"), |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 144 | "cc_library(name='rule1',", |
| 145 | " deps=['//b:rule2'])"); |
| 146 | } |
| 147 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 148 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 149 | public void testTwoRuleCycle2() throws Exception { |
| 150 | reporter.removeHandler(failFastHandler); // expect errors |
| 151 | scratch.file( |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 152 | "x/BUILD", |
| 153 | """ |
Googler | 83830c2 | 2024-10-05 08:45:04 -0700 | [diff] [blame] | 154 | load("@rules_java//java:defs.bzl", "java_library") |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 155 | java_library( |
| 156 | name = "x", |
| 157 | deps = ["y"], |
| 158 | ) |
| 159 | |
| 160 | java_library( |
| 161 | name = "y", |
| 162 | deps = ["x"], |
| 163 | ) |
| 164 | """); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 165 | getConfiguredTarget("//x"); |
| 166 | assertContainsEvent("in java_library rule //x:x: cycle in dependency graph"); |
| 167 | } |
| 168 | |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 169 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 170 | public void testIndirectOneRuleCycle() throws Exception { |
| 171 | scratchRule( |
| 172 | "cycle", |
| 173 | "foo.h", |
| 174 | "genrule(name = 'foo.h',", |
| 175 | " outs = ['bar.h'],", |
| 176 | " srcs = ['foo.h'],", |
| 177 | " cmd = 'cp $< $@')"); |
| 178 | checkError( |
| 179 | "main", |
| 180 | "mygenrule", |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 181 | // error message |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 182 | selfEdgeMsg("//cycle:foo.h"), |
| 183 | // Rule |
| 184 | "genrule(name='mygenrule',", |
| 185 | " outs = ['baz.h'],", |
| 186 | " srcs = ['//cycle:foo.h'],", |
| 187 | " cmd = 'cp $< $@')"); |
| 188 | } |
| 189 | |
John Cater | 660e8a5 | 2021-04-20 08:00:56 -0700 | [diff] [blame] | 190 | private Pattern selfEdgeMsg(String label) { |
| 191 | return Pattern.compile(label + " \\([a-f0-9]+|null\\) \\[self-edge\\]"); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | // Regression test for: "IllegalStateException in |
| 195 | // AbstractConfiguredTarget.initialize()". |
| 196 | // Failure to mark all cycle-forming nodes when there are *two* cycles led to |
| 197 | // an attempt to initialise a node we'd already visited. |
Florian Weikert | 10df952 | 2015-11-26 15:00:59 +0000 | [diff] [blame] | 198 | @Test |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 199 | public void testTwoCycles() throws Exception { |
| 200 | reporter.removeHandler(failFastHandler); // expect errors |
| 201 | scratch.file( |
| 202 | "x/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 203 | """ |
| 204 | genrule( |
| 205 | name = "b", |
| 206 | srcs = ["c"], |
| 207 | outs = ["b.out"], |
| 208 | cmd = ":", |
| 209 | tools = ["c"], |
| 210 | ) |
| 211 | |
| 212 | genrule( |
| 213 | name = "c", |
| 214 | srcs = ["b.out"], |
| 215 | outs = [], |
| 216 | cmd = ":", |
| 217 | ) |
| 218 | """); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 219 | getConfiguredTarget("//x:b"); // doesn't crash! |
Lukacs Berki | bba75d8 | 2016-06-14 09:08:29 +0000 | [diff] [blame] | 220 | assertContainsEvent("cycle in dependency graph"); |
| 221 | } |
| 222 | |
| 223 | @Test |
| 224 | public void testAspectCycle() throws Exception { |
| 225 | reporter.removeHandler(failFastHandler); |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 226 | scratch.file( |
| 227 | "x/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 228 | """ |
| 229 | load("//x:x.bzl", "aspected", "plain") |
| 230 | |
| 231 | # Using data= makes the dependency graph clearer because then the aspect does not propagate |
| 232 | # from aspectdep through a to b (and c) |
| 233 | plain( |
| 234 | name = "a", |
| 235 | noaspect_deps = [":b"], |
| 236 | ) |
| 237 | |
| 238 | aspected( |
| 239 | name = "b", |
| 240 | aspect_deps = ["c"], |
| 241 | ) |
| 242 | |
| 243 | plain(name = "c") |
| 244 | |
| 245 | plain( |
| 246 | name = "aspectdep", |
| 247 | aspect_deps = ["a"], |
| 248 | ) |
| 249 | """); |
Lukacs Berki | bba75d8 | 2016-06-14 09:08:29 +0000 | [diff] [blame] | 250 | |
cparsons | 0f22a96 | 2019-04-18 10:25:37 -0700 | [diff] [blame] | 251 | scratch.file( |
| 252 | "x/x.bzl", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 253 | """ |
| 254 | def _impl(ctx): |
| 255 | return [] |
| 256 | |
| 257 | rule_aspect = aspect( |
| 258 | implementation = _impl, |
| 259 | attr_aspects = ["aspect_deps"], |
| 260 | attrs = {"_implicit": attr.label(default = Label("//x:aspectdep"))}, |
| 261 | ) |
| 262 | |
| 263 | plain = rule( |
| 264 | implementation = _impl, |
| 265 | attrs = {"aspect_deps": attr.label_list(), "noaspect_deps": attr.label_list()}, |
| 266 | ) |
| 267 | |
| 268 | aspected = rule( |
| 269 | implementation = _impl, |
| 270 | attrs = {"aspect_deps": attr.label_list(aspects = [rule_aspect])}, |
| 271 | ) |
| 272 | """); |
Lukacs Berki | bba75d8 | 2016-06-14 09:08:29 +0000 | [diff] [blame] | 273 | |
| 274 | getConfiguredTarget("//x:a"); |
| 275 | assertContainsEvent("cycle in dependency graph"); |
| 276 | assertContainsEvent("//x:c with aspect //x:x.bzl%rule_aspect"); |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 277 | } |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 278 | |
| 279 | /** A late bound dependency which depends on the 'dep' label if the 'define' is in --defines. */ |
| 280 | // TODO(b/65746853): provide a way to do this without passing the entire configuration |
jhorvitz | 33f7648 | 2021-10-28 10:13:26 -0700 | [diff] [blame] | 281 | private static final LabelLateBoundDefault<BuildConfigurationValue> LATE_BOUND_DEP = |
janakr | a56a6ad | 2018-02-02 15:52:22 -0800 | [diff] [blame] | 282 | LabelLateBoundDefault.fromTargetConfiguration( |
jhorvitz | 33f7648 | 2021-10-28 10:13:26 -0700 | [diff] [blame] | 283 | BuildConfigurationValue.class, |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 284 | null, |
| 285 | (rule, attributes, config) -> |
| 286 | config.getCommandLineBuildVariables().containsKey(attributes.get("define", STRING)) |
| 287 | ? attributes.get("dep", NODEP_LABEL) |
| 288 | : null); |
| 289 | |
| 290 | /** A rule which always depends on the given label. */ |
| 291 | private static final MockRule NORMAL_DEPENDER = |
| 292 | () -> MockRule.define("normal_dep", attr("dep", LABEL).allowedFileTypes()); |
| 293 | |
| 294 | /** A rule which depends on a given label only if the given define is set. */ |
| 295 | private static final MockRule LATE_BOUND_DEPENDER = |
| 296 | () -> |
| 297 | MockRule.define( |
| 298 | "late_bound_dep", |
| 299 | attr("define", STRING).mandatory(), |
| 300 | attr("dep", NODEP_LABEL).mandatory(), |
| 301 | attr(":late_bound_dep", LABEL).value(LATE_BOUND_DEP)); |
| 302 | |
| 303 | /** A rule which removes a define from the configuration of its dependency. */ |
| 304 | private static final MockRule DEFINE_CLEARER = |
| 305 | () -> |
| 306 | MockRule.define( |
| 307 | "define_clearer", |
| 308 | attr("define", STRING).mandatory(), |
| 309 | attr("dep", LABEL) |
| 310 | .mandatory() |
| 311 | .allowedFileTypes() |
| 312 | .cfg( |
Googler | 81ca4aa | 2024-05-10 09:07:38 -0700 | [diff] [blame] | 313 | new TransitionFactory<>() { |
cparsons | 21e2518 | 2019-01-15 16:00:26 -0800 | [diff] [blame] | 314 | @Override |
John Cater | 2c0dece | 2019-04-02 09:18:18 -0700 | [diff] [blame] | 315 | public SplitTransition create(AttributeTransitionData data) { |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 316 | return new SplitTransition() { |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 317 | |
| 318 | @Override |
| 319 | public ImmutableSet<Class<? extends FragmentOptions>> |
| 320 | requiresOptionFragments() { |
| 321 | return ImmutableSet.of(CoreOptions.class); |
| 322 | } |
| 323 | |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 324 | @Override |
| 325 | public Map<String, BuildOptions> split( |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 326 | BuildOptionsView options, EventHandler eventHandler) { |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 327 | String define = data.attributes().get("define", STRING); |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 328 | BuildOptionsView newOptions = options.clone(); |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 329 | CoreOptions optionsFragment = newOptions.get(CoreOptions.class); |
| 330 | optionsFragment.commandLineBuildVariables = |
| 331 | optionsFragment.commandLineBuildVariables.stream() |
| 332 | .filter((pair) -> !pair.getKey().equals(define)) |
| 333 | .collect(toImmutableList()); |
gregce | 06b76c9 | 2020-06-19 14:46:56 -0700 | [diff] [blame] | 334 | return ImmutableMap.of("define_cleaner", newOptions.underlying()); |
gregce | ecb61ee | 2020-05-19 10:56:29 -0700 | [diff] [blame] | 335 | } |
cparsons | 21e2518 | 2019-01-15 16:00:26 -0800 | [diff] [blame] | 336 | }; |
| 337 | } |
John Cater | 0a9e1ed | 2019-03-27 11:02:01 -0700 | [diff] [blame] | 338 | |
| 339 | @Override |
Googler | 81ca4aa | 2024-05-10 09:07:38 -0700 | [diff] [blame] | 340 | public TransitionType transitionType() { |
| 341 | return TransitionType.ATTRIBUTE; |
| 342 | } |
| 343 | |
| 344 | @Override |
John Cater | 0a9e1ed | 2019-03-27 11:02:01 -0700 | [diff] [blame] | 345 | public boolean isSplit() { |
| 346 | return true; |
| 347 | } |
cparsons | 21e2518 | 2019-01-15 16:00:26 -0800 | [diff] [blame] | 348 | })); |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 349 | |
| 350 | @Override |
brandjon | 232a6b8 | 2020-06-08 12:32:49 -0700 | [diff] [blame] | 351 | protected ConfiguredRuleClassProvider createRuleClassProvider() { |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 352 | ConfiguredRuleClassProvider.Builder builder = |
| 353 | new ConfiguredRuleClassProvider.Builder() |
| 354 | .addRuleDefinition(NORMAL_DEPENDER) |
| 355 | .addRuleDefinition(LATE_BOUND_DEPENDER) |
| 356 | .addRuleDefinition(DEFINE_CLEARER); |
| 357 | TestRuleClassProvider.addStandardRules(builder); |
| 358 | return builder.build(); |
| 359 | } |
| 360 | |
| 361 | @Test |
| 362 | public void testLateBoundTargetCycleNotConfiguredTargetCycle() throws Exception { |
| 363 | // Target graph: //a -> //b -?> //c -> //a (loop) |
| 364 | // Configured target graph: //a -> //b -> //c -> //a (2) -> //b (2) |
| 365 | scratch.file("a/BUILD", "normal_dep(name = 'a', dep = '//b')"); |
| 366 | scratch.file("b/BUILD", "late_bound_dep(name = 'b', dep = '//c', define = 'CYCLE_ON')"); |
| 367 | scratch.file("c/BUILD", "define_clearer(name = 'c', dep = '//a', define = 'CYCLE_ON')"); |
| 368 | |
| 369 | useConfiguration("--define=CYCLE_ON=yes"); |
| 370 | getConfiguredTarget("//a"); |
| 371 | assertNoEvents(); |
| 372 | } |
| 373 | |
| 374 | @Test |
| 375 | public void testSelectTargetCycleNotConfiguredTargetCycle() throws Exception { |
| 376 | // Target graph: //a -> //b -?> //c -> //a (loop) |
| 377 | // Configured target graph: //a -> //b -> //c -> //a (2) -> //b (2) -> //b:stop (2) |
| 378 | scratch.file("a/BUILD", "normal_dep(name = 'a', dep = '//b')"); |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 379 | scratch.file( |
| 380 | "b/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 381 | """ |
| 382 | config_setting( |
| 383 | name = "cycle", |
| 384 | define_values = {"CYCLE_ON": "yes"}, |
| 385 | ) |
| 386 | |
| 387 | normal_dep(name = "stop") |
| 388 | |
| 389 | normal_dep( |
| 390 | name = "b", |
| 391 | dep = select({ |
| 392 | ":cycle": "//c", |
| 393 | "//conditions:default": ":stop", |
| 394 | }), |
| 395 | ) |
| 396 | """); |
mstaib | 16514d3 | 2017-12-07 15:18:52 -0800 | [diff] [blame] | 397 | scratch.file("c/BUILD", "define_clearer(name = 'c', dep = '//a', define = 'CYCLE_ON')"); |
| 398 | |
| 399 | useConfiguration("--define=CYCLE_ON=yes"); |
| 400 | getConfiguredTarget("//a"); |
| 401 | assertNoEvents(); |
| 402 | } |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 403 | |
| 404 | @Test |
| 405 | public void testInvalidVisibility() throws Exception { |
| 406 | scratch.file( |
| 407 | "a/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 408 | """ |
| 409 | cc_library( |
| 410 | name = "rule1", |
| 411 | visibility = ["//b:rule2"], |
| 412 | deps = ["//b:rule2"], |
| 413 | ) |
| 414 | """); |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 415 | scratch.file("b/BUILD", "cc_library(name='rule2')"); |
| 416 | |
| 417 | AssertionError expected = |
| 418 | assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:rule1")); |
| 419 | |
| 420 | assertThat(expected) |
| 421 | .hasMessageThat() |
| 422 | .contains("Label '//b:rule2' does not refer to a package group."); |
| 423 | } |
| 424 | |
| 425 | @Test |
| 426 | public void testInvalidVisibilityWithSelect() throws Exception { |
| 427 | scratch.file( |
| 428 | "a/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 429 | """ |
| 430 | cc_library( |
| 431 | name = "rule1", |
| 432 | visibility = ["//b:rule2"], |
| 433 | deps = ["//b:rule2"], |
| 434 | ) |
| 435 | """); |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 436 | scratch.file( |
| 437 | "b/BUILD", |
Googler | cdb03cc | 2024-03-27 12:02:12 -0700 | [diff] [blame] | 438 | """ |
| 439 | config_setting( |
| 440 | name = "fastbuild", |
| 441 | values = {"compilation_mode": "fastbuild"}, |
| 442 | ) |
| 443 | |
| 444 | cc_library( |
| 445 | name = "rule2", |
| 446 | hdrs = select({ |
| 447 | ":fastbuild": glob( |
| 448 | [ |
| 449 | "*.h", |
| 450 | ], |
| 451 | allow_empty = True, |
| 452 | ), |
| 453 | }), |
| 454 | ) |
| 455 | """); |
Googler | e10873c | 2021-10-20 09:09:01 -0700 | [diff] [blame] | 456 | |
| 457 | AssertionError expected = |
| 458 | assertThrows(AssertionError.class, () -> getConfiguredTarget("//a:rule1")); |
| 459 | |
| 460 | assertThat(expected) |
| 461 | .hasMessageThat() |
| 462 | .contains("Label '//b:rule2' does not refer to a package group."); |
| 463 | } |
Dmitry Lomov | f4cd475 | 2015-11-23 17:50:57 +0000 | [diff] [blame] | 464 | } |