Skip to content

Commit b80b8fd

Browse files
authored
fix: respect kind mapping (#1158)
When using the kind `gazelle:map_kind` directive, `gazelle` will correctly generate the buildfile on the first pass (or if no target of that type / name are present). However, when running gazelle a second time (or if a target of the mapped kind with the same name is present), `gazelle` will error out saying that it kind create a target of the original kind because a target of mapped kind is present and has the same name. Ex: Given the directive `# gazelle:map_kind py_test py_pytest_test //src/bazel/rules/python:py_pytest_test.bzl`, `gazelle` will correctly generate a `py_pytest_test` target where it would have generated a `py_test` target. But on a second invocation of `gazelle` (and subsequent invocations) it will error our with: ``` gazelle: ERROR: failed to generate target "//test/python/common:common_test" of kind "py_test": a target of kind "py_pytest_test" with the same name already exists. Use the '# gazelle:python_test_naming_convention' directive to change the naming convention. ```
1 parent 86eadf1 commit b80b8fd

File tree

9 files changed

+134
-8
lines changed

9 files changed

+134
-8
lines changed

gazelle/python/generate.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ var (
4646
buildFilenames = []string{"BUILD", "BUILD.bazel"}
4747
)
4848

49+
func GetActualKindName(kind string, args language.GenerateArgs) string {
50+
if kindOverride, ok := args.Config.KindMap[kind]; ok {
51+
return kindOverride.KindName
52+
}
53+
return kind
54+
}
55+
4956
// GenerateRules extracts build metadata from source files in a directory.
5057
// GenerateRules is called in each directory where an update is requested
5158
// in depth-first post-order.
@@ -70,6 +77,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
7077
}
7178
}
7279

80+
actualPyBinaryKind := GetActualKindName(pyBinaryKind, args)
81+
actualPyLibraryKind := GetActualKindName(pyLibraryKind, args)
82+
actualPyTestKind := GetActualKindName(pyTestKind, args)
83+
7384
pythonProjectRoot := cfg.PythonProjectRoot()
7485

7586
packageName := filepath.Base(args.Dir)
@@ -217,12 +228,12 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
217228
// generate it correctly.
218229
if args.File != nil {
219230
for _, t := range args.File.Rules {
220-
if t.Name() == pyLibraryTargetName && t.Kind() != pyLibraryKind {
231+
if t.Name() == pyLibraryTargetName && t.Kind() != actualPyLibraryKind {
221232
fqTarget := label.New("", args.Rel, pyLibraryTargetName)
222233
err := fmt.Errorf("failed to generate target %q of kind %q: "+
223234
"a target of kind %q with the same name already exists. "+
224235
"Use the '# gazelle:%s' directive to change the naming convention.",
225-
fqTarget.String(), pyLibraryKind, t.Kind(), pythonconfig.LibraryNamingConvention)
236+
fqTarget.String(), actualPyLibraryKind, t.Kind(), pythonconfig.LibraryNamingConvention)
226237
collisionErrors.Add(err)
227238
}
228239
}
@@ -253,12 +264,12 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
253264
// generate it correctly.
254265
if args.File != nil {
255266
for _, t := range args.File.Rules {
256-
if t.Name() == pyBinaryTargetName && t.Kind() != pyBinaryKind {
267+
if t.Name() == pyBinaryTargetName && t.Kind() != actualPyBinaryKind {
257268
fqTarget := label.New("", args.Rel, pyBinaryTargetName)
258269
err := fmt.Errorf("failed to generate target %q of kind %q: "+
259270
"a target of kind %q with the same name already exists. "+
260271
"Use the '# gazelle:%s' directive to change the naming convention.",
261-
fqTarget.String(), pyBinaryKind, t.Kind(), pythonconfig.BinaryNamingConvention)
272+
fqTarget.String(), actualPyBinaryKind, t.Kind(), pythonconfig.BinaryNamingConvention)
262273
collisionErrors.Add(err)
263274
}
264275
}
@@ -290,11 +301,11 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
290301
// generate it correctly.
291302
if args.File != nil {
292303
for _, t := range args.File.Rules {
293-
if t.Name() == conftestTargetname && t.Kind() != pyLibraryKind {
304+
if t.Name() == conftestTargetname && t.Kind() != actualPyLibraryKind {
294305
fqTarget := label.New("", args.Rel, conftestTargetname)
295306
err := fmt.Errorf("failed to generate target %q of kind %q: "+
296307
"a target of kind %q with the same name already exists.",
297-
fqTarget.String(), pyLibraryKind, t.Kind())
308+
fqTarget.String(), actualPyLibraryKind, t.Kind())
298309
collisionErrors.Add(err)
299310
}
300311
}
@@ -325,12 +336,12 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
325336
// generate it correctly.
326337
if args.File != nil {
327338
for _, t := range args.File.Rules {
328-
if t.Name() == pyTestTargetName && t.Kind() != pyTestKind {
339+
if t.Name() == pyTestTargetName && t.Kind() != actualPyTestKind {
329340
fqTarget := label.New("", args.Rel, pyTestTargetName)
330341
err := fmt.Errorf("failed to generate target %q of kind %q: "+
331342
"a target of kind %q with the same name already exists. "+
332343
"Use the '# gazelle:%s' directive to change the naming convention.",
333-
fqTarget.String(), pyTestKind, t.Kind(), pythonconfig.TestNamingConvention)
344+
fqTarget.String(), actualPyTestKind, t.Kind(), pythonconfig.TestNamingConvention)
334345
collisionErrors.Add(err)
335346
}
336347
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@rules_python//python:defs.bzl", "py_library")
2+
3+
# gazelle:map_kind py_test my_test :mytest.bzl
4+
5+
py_library(
6+
name = "respect_kind_mapping",
7+
srcs = ["__init__.py"],
8+
)
9+
10+
my_test(
11+
name = "respect_kind_mapping_test",
12+
srcs = ["__test__.py"],
13+
main = "__test__.py",
14+
deps = [":respect_kind_mapping"],
15+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load(":mytest.bzl", "my_test")
2+
load("@rules_python//python:defs.bzl", "py_library")
3+
4+
# gazelle:map_kind py_test my_test :mytest.bzl
5+
6+
py_library(
7+
name = "respect_kind_mapping",
8+
srcs = [
9+
"__init__.py",
10+
"foo.py",
11+
],
12+
visibility = ["//:__subpackages__"],
13+
)
14+
15+
my_test(
16+
name = "respect_kind_mapping_test",
17+
srcs = ["__test__.py"],
18+
main = "__test__.py",
19+
deps = [":respect_kind_mapping"],
20+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Respect Kind Mapping
2+
3+
This test case asserts that when using a kind mapping, gazelle will respect that mapping when parsing a BUILD file containing a mapped kind.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This is a Bazel workspace for the Gazelle test data.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2023 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+
from foo import foo
16+
17+
_ = foo
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2023 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+
import unittest
16+
17+
from __init__ import foo
18+
19+
20+
class FooTest(unittest.TestCase):
21+
def test_foo(self):
22+
self.assertEqual("foo", foo())
23+
24+
25+
if __name__ == "__main__":
26+
unittest.main()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright 2023 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+
def foo():
16+
return "foo"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2023 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+
---
16+
expect:
17+
exit_code: 0

0 commit comments

Comments
 (0)