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"Implementation of py_package rule" 16 17def _path_inside_wheel(input_file): 18 # input_file.short_path is sometimes relative ("../${repository_root}/foobar") 19 # which is not a valid path within a zip file. Fix that. 20 short_path = input_file.short_path 21 if short_path.startswith("..") and len(short_path) >= 3: 22 # Path separator. '/' on linux. 23 separator = short_path[2] 24 25 # Consume '../' part. 26 short_path = short_path[3:] 27 28 # Find position of next '/' and consume everything up to that character. 29 pos = short_path.find(separator) 30 short_path = short_path[pos + 1:] 31 return short_path 32 33def _py_package_impl(ctx): 34 inputs = depset( 35 transitive = [dep[DefaultInfo].data_runfiles.files for dep in ctx.attr.deps] + 36 [dep[DefaultInfo].default_runfiles.files for dep in ctx.attr.deps], 37 ) 38 39 # TODO: '/' is wrong on windows, but the path separator is not available in starlark. 40 # Fix this once ctx.configuration has directory separator information. 41 packages = [p.replace(".", "/") for p in ctx.attr.packages] 42 if not packages: 43 filtered_inputs = inputs 44 else: 45 filtered_files = [] 46 47 # TODO: flattening depset to list gives poor performance, 48 for input_file in inputs.to_list(): 49 wheel_path = _path_inside_wheel(input_file) 50 for package in packages: 51 if wheel_path.startswith(package): 52 filtered_files.append(input_file) 53 filtered_inputs = depset(direct = filtered_files) 54 55 return [DefaultInfo( 56 files = filtered_inputs, 57 )] 58 59py_package_lib = struct( 60 implementation = _py_package_impl, 61 attrs = { 62 "deps": attr.label_list( 63 doc = "", 64 ), 65 "packages": attr.string_list( 66 mandatory = False, 67 allow_empty = True, 68 doc = """\ 69List of Python packages to include in the distribution. 70Sub-packages are automatically included. 71""", 72 ), 73 }, 74 path_inside_wheel = _path_inside_wheel, 75) 76