Skip to content

Commit f6d6483

Browse files
committed
refact
1 parent 69c53ee commit f6d6483

File tree

7 files changed

+260
-44
lines changed

7 files changed

+260
-44
lines changed

.bazelrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
# (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
55
# To update these lines, execute
66
# `bazel run @rules_bazel_integration_test//tools:update_deleted_packages`
7-
build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,rules_python-repro,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/another_module,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma,tests/modules/other/nspkg_single,tests/modules/other/simple_v1,tests/modules/other/simple_v2,tests/modules/other/with_external_data,tests/whl_with_build_files/testdata,tests/whl_with_build_files/testdata/somepkg,tests/whl_with_build_files/testdata/somepkg-1.0.dist-info,tests/whl_with_build_files/testdata/somepkg/subpkg
8-
query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,rules_python-repro,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/another_module,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma,tests/modules/other/nspkg_single,tests/modules/other/simple_v1,tests/modules/other/simple_v2,tests/modules/other/with_external_data,tests/whl_with_build_files/testdata,tests/whl_with_build_files/testdata/somepkg,tests/whl_with_build_files/testdata/somepkg-1.0.dist-info,tests/whl_with_build_files/testdata/somepkg/subpkg
7+
build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/docs,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/another_module,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma,tests/modules/other/nspkg_single,tests/modules/other/simple_v1,tests/modules/other/simple_v2,tests/modules/other/with_external_data,tests/whl_with_build_files/testdata,tests/whl_with_build_files/testdata/somepkg,tests/whl_with_build_files/testdata/somepkg-1.0.dist-info,tests/whl_with_build_files/testdata/somepkg/subpkg
8+
query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/docs,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered,tests/modules/another_module,tests/modules/other,tests/modules/other/nspkg_delta,tests/modules/other/nspkg_gamma,tests/modules/other/nspkg_single,tests/modules/other/simple_v1,tests/modules/other/simple_v2,tests/modules/other/with_external_data,tests/whl_with_build_files/testdata,tests/whl_with_build_files/testdata/somepkg,tests/whl_with_build_files/testdata/somepkg-1.0.dist-info,tests/whl_with_build_files/testdata/somepkg/subpkg
99

1010
test --test_output=errors
1111

examples/build_file_generation/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ modules_mapping(
2929
"(\\.tests)+", # Add a custom one to get rid of the psutil tests.
3030
],
3131
# Uncomment the next line to enable hermetic builds across platforms
32-
# by ignoring platform-specific native libraries (useful for opencv-python-headless, etc.)
33-
# ignore_native_libs = True,
32+
# by skipping private shared objects under .libs directories (useful for opencv-python-headless, etc.)
33+
# skip_private_shared_objects = True,
3434
wheels = all_whl_requirements,
3535
)
3636

gazelle/docs/installation_and_usage.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,14 @@ modules_mapping(
9797
# reducing manual overhead in managing separate stub packages.
9898
include_stub_packages = True,
9999

100-
# ignore_native_libs: bool (default: False)
101-
# If set to True, this flag ignores platform-specific native libraries (.so files)
102-
# when generating the modules mapping. This ensures hermetic builds across different
103-
# platforms (Linux, macOS, etc.) by producing identical manifests regardless of
104-
# platform-specific wheel contents. Useful for packages like opencv-python-headless
105-
# that include different native libraries on different platforms.
106-
ignore_native_libs = True,
100+
# skip_private_shared_objects: bool (default: False)
101+
# If set to True, this flag skips private shared objects under .libs directories
102+
# when generating the modules mapping. These are non-importable dependency libraries
103+
# (like libopenblas.so) that vary between Linux distributions and break build
104+
# hermiticity. Ensures identical manifests across platforms by excluding libraries
105+
# that cannot be imported in Python code. macOS uses .dylib files which are
106+
# naturally excluded by this Linux-specific .so filtering.
107+
skip_private_shared_objects = True,
107108
)
108109

109110
# Gazelle python extension needs a manifest file mapping from

gazelle/modules_mapping/def.bzl

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ def _modules_mapping_impl(ctx):
3838
args.set_param_file_format(format = "multiline")
3939
if ctx.attr.include_stub_packages:
4040
args.add("--include_stub_packages")
41-
if ctx.attr.ignore_native_libs:
42-
args.add("--ignore_native_libs")
41+
if ctx.attr.skip_private_shared_objects:
42+
args.add("--skip_private_shared_objects")
4343
args.add("--output_file", modules_mapping)
4444
args.add_all("--exclude_patterns", ctx.attr.exclude_patterns)
4545
args.add_all("--wheels", all_wheels)
@@ -66,19 +66,19 @@ modules_mapping = rule(
6666
doc = "Whether to include stub packages in the mapping.",
6767
mandatory = False,
6868
),
69-
"ignore_native_libs": attr.bool(
70-
default = False,
71-
doc = "Whether to ignore platform-specific native libraries (*.so files) when generating mappings. " +
72-
"When True, ensures hermetic builds across different platforms by excluding native library " +
73-
"mappings that vary between Linux, macOS, etc. Useful for packages like opencv-python-headless " +
74-
"that bundle different native libraries on different platforms.",
75-
mandatory = False,
76-
),
7769
"modules_mapping_name": attr.string(
7870
default = "modules_mapping.json",
7971
doc = "The name for the output JSON file.",
8072
mandatory = False,
8173
),
74+
"skip_private_shared_objects": attr.bool(
75+
default = False,
76+
doc = "Whether to skip private shared objects under .libs directories when generating mappings. " +
77+
"When True, excludes non-importable dependency libraries (like libopenblas.so) that vary " +
78+
"between Linux platforms and break build hermiticity. These .libs files are not actual " +
79+
"Python modules and cannot be imported. macOS uses .dylib files which are naturally excluded.",
80+
mandatory = False,
81+
),
8282
"wheels": attr.label_list(
8383
allow_files = True,
8484
doc = "The list of wheels, usually the 'all_whl_requirements' from @<pip_repository>//:requirements.bzl",

gazelle/modules_mapping/generator.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,19 @@ class Generator:
2626
output_file = None
2727
excluded_patterns = None
2828

29-
def __init__(self, stderr, output_file, excluded_patterns, include_stub_packages, ignore_native_libs=False):
29+
def __init__(
30+
self,
31+
stderr,
32+
output_file,
33+
excluded_patterns,
34+
include_stub_packages,
35+
skip_private_shared_objects=False,
36+
):
3037
self.stderr = stderr
3138
self.output_file = output_file
3239
self.excluded_patterns = [re.compile(pattern) for pattern in excluded_patterns]
3340
self.include_stub_packages = include_stub_packages
34-
self.ignore_native_libs = ignore_native_libs
41+
self.skip_private_shared_objects = skip_private_shared_objects
3542
self.mapping = {}
3643

3744
# dig_wheel analyses the wheel .whl file determining the modules it provides
@@ -75,9 +82,13 @@ def simplify(self):
7582
def module_for_path(self, path, whl):
7683
ext = pathlib.Path(path).suffix
7784
if ext == ".py" or ext == ".so":
78-
# Skip native libraries if ignore_native_libs is enabled
79-
if ext == ".so" and self.ignore_native_libs:
80-
return
85+
# Skip private shared objects under .libs directories on Linux.
86+
# These are non-importable dependency libraries (like libopenblas.so) that vary
87+
# between platforms and make builds non-hermetic. macOS uses .dylib files
88+
# which are naturally excluded by the .so check.
89+
if ext == ".so" and self.skip_private_shared_objects:
90+
if ".libs/" in path or path.split("/")[0].endswith(".libs"):
91+
return
8192

8293
if "purelib" in path or "platlib" in path:
8394
root = "/".join(path.split("/")[2:])
@@ -163,11 +174,15 @@ def data_has_purelib_or_platlib(path):
163174
)
164175
parser.add_argument("--output_file", type=str)
165176
parser.add_argument("--include_stub_packages", action="store_true")
166-
parser.add_argument("--ignore_native_libs", action="store_true")
177+
parser.add_argument("--skip_private_shared_objects", action="store_true")
167178
parser.add_argument("--exclude_patterns", nargs="+", default=[])
168179
parser.add_argument("--wheels", nargs="+", default=[])
169180
args = parser.parse_args()
170181
generator = Generator(
171-
sys.stderr, args.output_file, args.exclude_patterns, args.include_stub_packages, args.ignore_native_libs
182+
sys.stderr,
183+
args.output_file,
184+
args.exclude_patterns,
185+
args.include_stub_packages,
186+
args.skip_private_shared_objects,
172187
)
173188
sys.exit(generator.run(args.wheels))

gazelle/modules_mapping/test_generator.py

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,58 @@ def test_stub_excluded(self):
3939
gen.mapping.items(),
4040
)
4141

42-
def test_ignore_native_libs(self):
43-
# Test the ignore_native_libs functionality with the module_for_path method
44-
gen_with_native_libs = Generator(None, None, {}, False, False)
45-
gen_without_native_libs = Generator(None, None, {}, False, True)
42+
def test_skip_private_shared_objects(self):
43+
# Test the skip_private_shared_objects functionality with the module_for_path method
44+
gen_with_private_libs = Generator(None, None, {}, False, False)
45+
gen_without_private_libs = Generator(None, None, {}, False, True)
4646

47-
# Simulate a Python file - should be included in both cases
48-
gen_with_native_libs.module_for_path("cv2/__init__.py", "opencv_python_headless-4.8.1-cp310-cp310-linux_x86_64.whl")
49-
gen_without_native_libs.module_for_path("cv2/__init__.py", "opencv_python_headless-4.8.1-cp310-cp310-linux_x86_64.whl")
47+
# Simulate Python files - should be included in both cases
48+
gen_with_private_libs.module_for_path(
49+
"cv2/__init__.py",
50+
"opencv_python_headless-4.12.0.88-cp37-abi3-manylinux2014_x86_64.whl",
51+
)
52+
gen_without_private_libs.module_for_path(
53+
"cv2/__init__.py",
54+
"opencv_python_headless-4.12.0.88-cp37-abi3-manylinux2014_x86_64.whl",
55+
)
56+
gen_with_private_libs.module_for_path(
57+
"numpy/__init__.py", "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.whl"
58+
)
59+
gen_without_private_libs.module_for_path(
60+
"numpy/__init__.py", "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.whl"
61+
)
62+
63+
# Real-world examples from wheels
64+
private_shared_objects = [
65+
"opencv_python_headless.libs/libopenblas-r0-f650aae0.so",
66+
"numpy.libs/libscipy_openblas64_-56d6093b.so",
67+
]
68+
69+
# Add all private shared objects to both generators
70+
for lib_path in private_shared_objects:
71+
wheel_name = (
72+
"opencv_python_headless-4.12.0.88"
73+
if "opencv" in lib_path
74+
else "numpy-2.2.6"
75+
)
76+
gen_with_private_libs.module_for_path(lib_path, f"{wheel_name}.whl")
77+
gen_without_private_libs.module_for_path(lib_path, f"{wheel_name}.whl")
5078

51-
# Simulate a native library - should be included only when ignore_native_libs=False
52-
gen_with_native_libs.module_for_path("opencv_python_headless.libs/libopenblas-r0-f650aae0.so", "opencv_python_headless-4.8.1-cp310-cp310-linux_x86_64.whl")
53-
gen_without_native_libs.module_for_path("opencv_python_headless.libs/libopenblas-r0-f650aae0.so", "opencv_python_headless-4.8.1-cp310-cp310-linux_x86_64.whl")
79+
# Both should have the Python module mappings
80+
self.assertIn("cv2", gen_with_private_libs.mapping)
81+
self.assertIn("cv2", gen_without_private_libs.mapping)
82+
self.assertIn("numpy", gen_with_private_libs.mapping)
83+
self.assertIn("numpy", gen_without_private_libs.mapping)
5484

55-
# Both should have the Python module mapping
56-
self.assertIn("cv2", gen_with_native_libs.mapping)
57-
self.assertIn("cv2", gen_without_native_libs.mapping)
85+
# Only gen_with_private_libs should have the private shared object mappings
86+
expected_private_mappings = [
87+
"opencv_python_headless.libs.libopenblas-r0-f650aae0",
88+
"numpy.libs.libscipy_openblas64_-56d6093b",
89+
]
5890

59-
# Only gen_with_native_libs should have the native library mapping
60-
self.assertIn("opencv_python_headless.libs.libopenblas-r0-f650aae0", gen_with_native_libs.mapping)
61-
self.assertNotIn("opencv_python_headless.libs.libopenblas-r0-f650aae0", gen_without_native_libs.mapping)
91+
for mapping in expected_private_mappings:
92+
self.assertIn(mapping, gen_with_private_libs.mapping)
93+
self.assertNotIn(mapping, gen_without_private_libs.mapping)
6294

6395

6496
if __name__ == "__main__":

0 commit comments

Comments
 (0)