diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index c1edec4731..8cbe4575c1 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -310,10 +310,41 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey)) } } + + ruleBySrcs := make(map[string]string) + srcsByRule := make(map[string]*treeset.Set) + if args.File != nil { + for _, r := range args.File.Rules { + kindName := r.Kind() + // if kindInfo, ok := args.Config.KindMap[kindName]; ok { + // kindName = kindInfo.KindName + // } + + if kindName == actualPyLibraryKind { + srcsList := r.AttrStrings("srcs") + for _, src := range srcsList { + ruleBySrcs[src] = r.Name() + if srcsByRule[r.Name()] == nil { + srcsByRule[r.Name()] = treeset.NewWith(godsutils.StringComparator) + } + srcsByRule[r.Name()].Add(src) + } + } + } + } + if cfg.PerFileGeneration() { + // Handle new py_library targets hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir) pyLibraryFilenames.Each(func(index int, filename interface{}) { - pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py") + var pyLibraryTargetName string + if _, ok := ruleBySrcs[filename.(string)]; ok { + return + // pyLibraryTargetName = strings.TrimSuffix(filepath.Base(filename.(string)), ".py") + } else { + } + pyLibraryTargetName = strings.TrimSuffix(filepath.Base(filename.(string)), ".py") + if filename == pyLibraryEntrypointFilename && !nonEmptyInit { return // ignore empty __init__.py. } @@ -323,8 +354,35 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } appendPyLibrary(srcs, pyLibraryTargetName) }) + + // Handle existing py_library targets + for rule := range srcsByRule { + srcs := srcsByRule[rule] + appendPyLibrary(srcs, rule) + } + } else { - appendPyLibrary(pyLibraryFilenames, cfg.RenderLibraryName(packageName)) + + // Handle existing py_library targets + for rule := range srcsByRule { + srcs := srcsByRule[rule] + appendPyLibrary(srcs, rule) + } + + // Handle remaining files in one py_library target + // Create a new set with all the files not yet included in any rule + remaining := treeset.NewWith(godsutils.StringComparator) + pyLibraryFilenames.Each(func(index int, value interface{}) { + filename := value.(string) + if _, ok := ruleBySrcs[filename]; !ok { + remaining.Add(filename) + } + }) + + if !remaining.Empty() { + appendPyLibrary(remaining, cfg.RenderLibraryName(packageName)) + } + // appendPyLibrary(pyLibraryFilenames, cfg.RenderLibraryName(packageName)) } if hasPyBinaryEntryPointFile { diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.in b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.in new file mode 100644 index 0000000000..a82b04c50d --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.in @@ -0,0 +1,11 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:python_generation_mode file + +# This target should be maintained by gazelle (but should get a new deps). +py_library( + name = "custom", + srcs = ["bar.py", "baz.py"], + visibility = ["//visibility:private"], + tags = ["cant_touch_this"], +) diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.out b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.out new file mode 100644 index 0000000000..5c56d141ed --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/BUILD.out @@ -0,0 +1,32 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +# gazelle:python_generation_mode file + +# This target should be maintained by gazelle (but should get a new deps). +py_library( + name = "custom", + srcs = [ + "bar.py", + "baz.py", + ], + tags = ["cant_touch_this"], + visibility = ["//visibility:private"], + deps = [":foo"], +) + +py_library( + name = "foo", + srcs = ["foo.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "bar_test", + srcs = ["bar_test.py"], + deps = [":custom"], +) + +py_test( + name = "foo_test", + srcs = ["foo_test.py"], +) diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/README.md b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/README.md new file mode 100644 index 0000000000..37f476dd1d --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/README.md @@ -0,0 +1,5 @@ +# Per-file generation with existing target spanning multiple files + +This test case generates one `py_library` per file, but has an existing target containing 2 files. In this +case, the existing target should be preserved (and used for the existing 2 files), but new targets should be +created for new files. diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/WORKSPACE b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/__init__.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar.py @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar_test.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar_test.py new file mode 100644 index 0000000000..b6b8723822 --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/bar_test.py @@ -0,0 +1 @@ +import bar diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/baz.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/baz.py new file mode 100644 index 0000000000..492cbc0260 --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/baz.py @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +import foo diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/foo.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/foo.py new file mode 100644 index 0000000000..41010956cf --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/foo.py @@ -0,0 +1,13 @@ +# Copyright 2023 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. diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/foo_test.py b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/foo_test.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/test.yaml b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/per_file_respect_existing_multiple_srcs/test.yaml @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +--- diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.in b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.in new file mode 100644 index 0000000000..e4075255ab --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.in @@ -0,0 +1,18 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:python_generation_mode project + +# This target should be maintained and unchangedby gazelle +py_library( + name = "__init__", + srcs = ["__init__.py"], + visibility = ["//visibility:private"], +) + +# This target should be maintained by gazelle (but should get a new deps). +py_library( + name = "custom", + srcs = ["bar.py", "baz.py"], + visibility = ["//visibility:private"], + tags = ["cant_touch_this"], +) diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.out b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.out new file mode 100644 index 0000000000..c28aa4a617 --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/BUILD.out @@ -0,0 +1,37 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +# gazelle:python_generation_mode project + +# This target should be maintained and unchangedby gazelle +py_library( + name = "__init__", + srcs = ["__init__.py"], + visibility = ["//visibility:private"], +) + +# This target should be maintained by gazelle (but should get a new deps). +py_library( + name = "custom", + srcs = [ + "bar.py", + "baz.py", + ], + tags = ["cant_touch_this"], + visibility = ["//visibility:private"], + deps = [":project_generation_mode_respect_existing_multiple_srcs"], +) + +py_library( + name = "project_generation_mode_respect_existing_multiple_srcs", + srcs = ["foo.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "project_generation_mode_respect_existing_multiple_srcs_test", + srcs = [ + "bar_test.py", + "foo_test.py", + ], + deps = [":custom"], +) diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/README.md b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/README.md new file mode 100644 index 0000000000..6711a8aa7f --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/README.md @@ -0,0 +1,4 @@ +# Project generation with existing target spanning multiple files + +This test generates targets according to project mode, but it respects existing targets that have been created. +This should make it easier to incrementally migrate to project mode. diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/WORKSPACE b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/__init__.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar.py @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar_test.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar_test.py new file mode 100644 index 0000000000..b6b8723822 --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/bar_test.py @@ -0,0 +1 @@ +import bar diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/baz.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/baz.py new file mode 100644 index 0000000000..492cbc0260 --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/baz.py @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +import foo diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/foo.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/foo.py new file mode 100644 index 0000000000..41010956cf --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/foo.py @@ -0,0 +1,13 @@ +# Copyright 2023 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. diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/foo_test.py b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/foo_test.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/test.yaml b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/project_generation_mode_respect_existing_multiple_srcs/test.yaml @@ -0,0 +1,15 @@ +# Copyright 2023 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. + +---