@@ -369,6 +369,18 @@ def collect_runfiles(ctx, files = depset()):
369369 collect_default = True ,
370370 )
371371
372+ def _third_party_first (targets ):
373+ """Sort targets to allow for deterministic depset merging.
374+
375+ First return the third party deps so that the depsets get merged in a way to allow
376+ topological traversal so that the first dependency that is met during traversing
377+ will be a third party dep.
378+
379+ This is because the DAG is going from first-party deps to third-party deps and usually
380+ no third-party deps include first-party deps.
381+ """
382+ return sorted (targets , lambda x : PyInfo in x and not x [PyInfo ].package )
383+
372384def create_py_info (
373385 ctx ,
374386 * ,
@@ -420,20 +432,9 @@ def create_py_info(
420432 py_info .merge_has_py2_only_sources (ctx .attr .srcs_version in ("PY2" , "PY2ONLY" ))
421433 py_info .merge_has_py3_only_sources (ctx .attr .srcs_version in ("PY3" , "PY3ONLY" ))
422434
423- # First merge the third party deps so that the depsets get merged in a way to allow
424- # topological traversal so that the first dependency that is met during traversing
425- # will be a third party dep.
426- #
427- # This is because the DAG is going from first-party deps to third-party deps and usually
428- # no third-party deps include first-party deps.
429-
430- # TODO @aignas 2025-06-05: refactor the code
431- for target in ctx .attr .deps :
435+ for target in _third_party_first (ctx .attr .deps ):
432436 # PyInfo may not be present e.g. cc_library rules.
433437 if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target ):
434- if PyInfo in target and not target [PyInfo ].package :
435- continue
436-
437438 py_info .merge (_get_py_info (target ))
438439 else :
439440 # TODO(b/228692666): Remove this once non-PyInfo targets are no
@@ -443,41 +444,9 @@ def create_py_info(
443444 if f .extension == "py" :
444445 py_info .transitive_sources .add (f )
445446 py_info .merge_uses_shared_libraries (cc_helper .is_valid_shared_library_artifact (f ))
446- for target in ctx .attr .pyi_deps :
447+ for target in _third_party_first ( ctx .attr .pyi_deps ) :
447448 # PyInfo may not be present e.g. cc_library rules.
448449 if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target ):
449- if PyInfo in target and not target [PyInfo ].package :
450- continue
451-
452- py_info .merge (_get_py_info (target ))
453-
454- # Now proceed with the first party deps
455-
456- for target in ctx .attr .deps :
457- # PyInfo may not be present e.g. cc_library rules.
458- if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target ):
459- if PyInfo in target and target [PyInfo ].package :
460- continue
461- elif PyInfo not in target :
462- continue
463-
464- py_info .merge (_get_py_info (target ))
465- else :
466- # TODO(b/228692666): Remove this once non-PyInfo targets are no
467- # longer supported in `deps`.
468- files = target .files .to_list ()
469- for f in files :
470- if f .extension == "py" :
471- py_info .transitive_sources .add (f )
472- py_info .merge_uses_shared_libraries (cc_helper .is_valid_shared_library_artifact (f ))
473- for target in ctx .attr .pyi_deps :
474- # PyInfo may not be present e.g. cc_library rules.
475- if PyInfo in target or (BuiltinPyInfo != None and BuiltinPyInfo in target ):
476- if PyInfo in target and target [PyInfo ].package :
477- continue
478- elif PyInfo not in target :
479- continue
480-
481450 py_info .merge (_get_py_info (target ))
482451
483452 deps_transitive_sources = py_info .transitive_sources .build ()
0 commit comments