@@ -231,9 +231,14 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
231231 }
232232
233233 collisionErrors := singlylinkedlist .New ()
234+ // Create a validFilesMap of mainModules to validate if python macros have valid srcs.
235+ validFilesMap := make (map [string ]struct {})
234236
235237 appendPyLibrary := func (srcs * treeset.Set , pyLibraryTargetName string ) {
236238 allDeps , mainModules , annotations , err := parser .parse (srcs )
239+ for name := range mainModules {
240+ validFilesMap [name ] = struct {}{}
241+ }
237242 if err != nil {
238243 log .Fatalf ("ERROR: %v\n " , err )
239244 }
@@ -363,6 +368,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
363368 setAnnotations (* annotations ).
364369 generateImportsAttribute ()
365370
371+
366372 pyBinary := pyBinaryTarget .build ()
367373
368374 result .Gen = append (result .Gen , pyBinary )
@@ -490,7 +496,8 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
490496 result .Gen = append (result .Gen , pyTest )
491497 result .Imports = append (result .Imports , pyTest .PrivateAttr (config .GazelleImportsKey ))
492498 }
493-
499+ emptyRules := py .getRulesWithInvalidSrcs (args , validFilesMap )
500+ result .Empty = append (result .Empty , emptyRules ... )
494501 if ! collisionErrors .Empty () {
495502 it := collisionErrors .Iterator ()
496503 for it .Next () {
@@ -502,6 +509,42 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
502509 return result
503510}
504511
512+ // getRulesWithInvalidSrcs checks existing Python rules in the BUILD file and return the rules with invalid source files.
513+ // Invalid source files are files that do not exist or not a target.
514+ func (py * Python ) getRulesWithInvalidSrcs (args language.GenerateArgs , validFilesMap map [string ]struct {}) (invalidRules []* rule.Rule ) {
515+ if args .File == nil {
516+ return
517+ }
518+ for _ , file := range args .GenFiles {
519+ validFilesMap [file ] = struct {}{}
520+ }
521+
522+ isTarget := func (src string ) bool {
523+ return strings .HasPrefix (src , "@" ) || strings .HasPrefix (src , "//" ) || strings .HasPrefix (src , ":" )
524+ }
525+ for _ , existingRule := range args .File .Rules {
526+ actualPyBinaryKind := GetActualKindName (pyBinaryKind , args )
527+ if existingRule .Kind () != actualPyBinaryKind {
528+ continue
529+ }
530+ var hasValidSrcs bool
531+ for _ , src := range existingRule .AttrStrings ("srcs" ) {
532+ if isTarget (src ) {
533+ hasValidSrcs = true
534+ break
535+ }
536+ if _ , ok := validFilesMap [src ]; ok {
537+ hasValidSrcs = true
538+ break
539+ }
540+ }
541+ if ! hasValidSrcs {
542+ invalidRules = append (invalidRules , newTargetBuilder (pyBinaryKind , existingRule .Name (), "" , "" , nil , false ).build ())
543+ }
544+ }
545+ return invalidRules
546+ }
547+
505548// isBazelPackage determines if the directory is a Bazel package by probing for
506549// the existence of a known BUILD file name.
507550func isBazelPackage (dir string ) bool {
0 commit comments