@@ -29,6 +29,44 @@ PyWheelInfo = provider(
2929 "wheel" : "File: The wheel file itself." ,
3030 },
3131)
32+ DistInfo = provider (
33+ """Information about the requirements of a Python package.""" ,
34+ fields = ["metadata" ],
35+ )
36+
37+ def _aspect_impl (target , ctx ):
38+ """
39+ The main implementation of the aspect, orchestrating the collection and archiving of files.
40+
41+ Args:
42+ target: The target being processed.
43+ ctx: The context for this aspect's execution.
44+
45+ Returns:
46+ A list of `TarInfo` providers indicating the tar archives created by this aspect.
47+ """
48+
49+ metadata = []
50+ if target .label .workspace_root == "" :
51+ for dep in getattr (ctx .rule .attr , "deps" , []):
52+ if dep .label .workspace_root == "" :
53+ continue
54+ workspace_root = dep .label .workspace_root
55+ for file in dep [DefaultInfo ].data_runfiles .files .to_list ():
56+ if file .path .startswith (workspace_root ) and file .path .endswith (".dist-info/METADATA" ):
57+ metadata .append (file )
58+
59+ return [DistInfo (metadata = metadata )]
60+
61+ # The aspect itself, specifying how it applies to targets and what attributes it uses.
62+ distinfo_aspect = aspect (
63+ implementation = _aspect_impl ,
64+ attr_aspects = ["deps" ], # Indicates that this aspect should propagate along 'deps' attributes.
65+ toolchains = [
66+ str (Label ("@bazel_tools//tools/python:toolchain_type" )),
67+ ],
68+ doc = "An aspect for creating tar archives of transitive external dependencies, facilitating the bundling of these dependencies for container images or other deployment artifacts." ,
69+ )
3270
3371_distribution_attrs = {
3472 "abi" : attr .string (
@@ -231,6 +269,44 @@ _DESCRIPTION_FILE_EXTENSION_TO_TYPE = {
231269}
232270_DEFAULT_DESCRIPTION_FILE_TYPE = "text/plain"
233271
272+ def create_requirements_file (ctx ):
273+ """Write requirements to a file
274+
275+ Args:
276+ ctx: The context object.
277+ Returns:
278+ The file containing the requirements.
279+
280+ """
281+ all_metadatafiles = []
282+ args = ctx .actions .args ()
283+ for dep in ctx .attr .deps :
284+ if DistInfo in dep :
285+ all_metadatafiles .extend (dep [DistInfo ].metadata )
286+
287+ if len (all_metadatafiles ) == 0 :
288+ return None
289+
290+ for m in all_metadatafiles :
291+ args .add ("--metadata_file" , m )
292+ args .use_param_file (param_file_arg = "--param-file=%s" , use_always = True )
293+
294+ if hasattr (ctx .outputs , "out" ):
295+ requirements_file = ctx .outputs .out
296+ else :
297+ requirements_file = ctx .actions .declare_file (ctx .attr .name + "_requirements.txt" )
298+ args .add ("--output" , requirements_file )
299+
300+ ctx .actions .run (
301+ inputs = depset (direct = all_metadatafiles ),
302+ outputs = [requirements_file ],
303+ arguments = [args ],
304+ use_default_shell_env = True ,
305+ executable = ctx .executable ._extract_requirements ,
306+ progress_message = "Building requirements" ,
307+ )
308+ return requirements_file
309+
234310def _escape_filename_distribution_name (name ):
235311 """Escape the distribution name component of a filename.
236312
@@ -358,6 +434,10 @@ def _py_wheel_impl(ctx):
358434 other_inputs .extend ([ctx .version_file , ctx .info_file ])
359435
360436 args .add ("--input_file_list" , packageinputfile )
437+ requirements_file = create_requirements_file (ctx )
438+ if requirements_file :
439+ args .add ("--requirements" , requirements_file )
440+ other_inputs .append (requirements_file )
361441
362442 # Note: Description file and version are not embedded into metadata.txt yet,
363443 # it will be done later by wheelmaker script.
@@ -547,6 +627,7 @@ py_wheel_lib = struct(
547627 attrs = _concat_dicts (
548628 {
549629 "deps" : attr .label_list (
630+ aspects = [distinfo_aspect ],
550631 doc = """\
551632 Targets to be included in the distribution.
552633
0 commit comments