1# Copyright 2018 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 15load("@bazel_skylib//rules:build_test.bzl", "build_test") 16load("@bazel_skylib//rules:write_file.bzl", "write_file") 17load("//examples/wheel/private:wheel_utils.bzl", "directory_writer", "make_variable_tags") 18load("//python:defs.bzl", "py_library", "py_test") 19load("//python:packaging.bzl", "py_package", "py_wheel") 20load("//python:pip.bzl", "compile_pip_requirements") 21load("//python:versions.bzl", "gen_python_config_settings") 22load("//python/entry_points:py_console_script_binary.bzl", "py_console_script_binary") 23load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility 24 25package(default_visibility = ["//visibility:public"]) 26 27licenses(["notice"]) # Apache 2.0 28 29py_library( 30 name = "main", 31 srcs = ["main.py"], 32 deps = [ 33 "//examples/wheel/lib:simple_module", 34 "//examples/wheel/lib:module_with_data", 35 # Example dependency which is not packaged in the wheel 36 # due to "packages" filter on py_package rule. 37 "//tests/load_from_macro:foo", 38 ], 39) 40 41py_library( 42 name = "main_with_gen_data", 43 srcs = ["main.py"], 44 data = [ 45 ":gen_dir", 46 ], 47) 48 49directory_writer( 50 name = "gen_dir", 51 out = "someDir", 52 files = {"foo.py": ""}, 53) 54 55# Package just a specific py_libraries, without their dependencies 56py_wheel( 57 name = "minimal_with_py_library", 58 testonly = True, # Set this to verify the generated .dist target doesn't break things 59 # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl" 60 distribution = "example_minimal_library", 61 python_tag = "py3", 62 # NOTE: twine_binary = "//tools/publish:twine" does not work on non-bzlmod 63 # setups because the `//tools/publish:twine` produces multiple files and is 64 # unsuitable as the `src` to the underlying native_binary rule. 65 twine = None if BZLMOD_ENABLED else "@rules_python_publish_deps_twine//:pkg", 66 version = "0.0.1", 67 deps = [ 68 "//examples/wheel/lib:module_with_data", 69 "//examples/wheel/lib:simple_module", 70 ], 71) 72 73# Populate a rule with "Make Variable" arguments for 74# abi, python_tag and version. You might want to do this 75# for the following use cases: 76# - abi, python_tag: introspect a toolchain to map to appropriate cpython tags 77# - version: populate given this or a dependent module's version 78make_variable_tags( 79 name = "make_variable_tags", 80) 81 82py_wheel( 83 name = "minimal_with_py_library_with_make_variables", 84 testonly = True, 85 abi = "$(ABI)", 86 distribution = "example_minimal_library", 87 python_tag = "$(PYTHON_TAG)", 88 toolchains = ["//examples/wheel:make_variable_tags"], 89 version = "$(VERSION)", 90 deps = [ 91 "//examples/wheel/lib:module_with_data", 92 "//examples/wheel/lib:simple_module", 93 ], 94) 95 96build_test( 97 name = "dist_build_tests", 98 targets = [":minimal_with_py_library.dist"], 99) 100 101# Package just a specific py_libraries, without their dependencies 102py_wheel( 103 name = "minimal_with_py_library_with_stamp", 104 # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl" 105 distribution = "example_minimal_library{BUILD_USER}", 106 python_tag = "py3", 107 stamp = 1, 108 version = "0.1.{BUILD_TIMESTAMP}", 109 deps = [ 110 "//examples/wheel/lib:module_with_data", 111 "//examples/wheel/lib:simple_module", 112 ], 113) 114 115# Use py_package to collect all transitive dependencies of a target, 116# selecting just the files within a specific python package. 117py_package( 118 name = "example_pkg", 119 # Only include these Python packages. 120 packages = ["examples.wheel"], 121 deps = [":main"], 122) 123 124py_package( 125 name = "example_pkg_with_data", 126 packages = ["examples.wheel"], 127 deps = [":main_with_gen_data"], 128) 129 130py_wheel( 131 name = "minimal_with_py_package", 132 # Package data. We're building "example_minimal_package-0.0.1-py3-none-any.whl" 133 distribution = "example_minimal_package", 134 python_tag = "py3", 135 version = "0.0.1", 136 deps = [":example_pkg"], 137) 138 139# An example that uses all features provided by py_wheel. 140py_wheel( 141 name = "customized", 142 author = "Example Author with non-ascii characters: żółw", 143 author_email = "[email protected]", 144 classifiers = [ 145 "License :: OSI Approved :: Apache Software License", 146 "Intended Audience :: Developers", 147 ], 148 console_scripts = { 149 "customized_wheel": "examples.wheel.main:main", 150 }, 151 description_file = "README.md", 152 # Package data. We're building "example_customized-0.0.1-py3-none-any.whl" 153 distribution = "example_customized", 154 entry_points = { 155 "console_scripts": ["another = foo.bar:baz"], 156 "group2": [ 157 "second = second.main:s", 158 "first = first.main:f", 159 ], 160 }, 161 extra_distinfo_files = { 162 "//examples/wheel:NOTICE": "NOTICE", 163 # Rename the file when packaging to show we can. 164 "//examples/wheel:README.md": "README", 165 }, 166 homepage = "www.example.com", 167 license = "Apache 2.0", 168 project_urls = { 169 "Bug Tracker": "www.example.com/issues", 170 "Documentation": "www.example.com/docs", 171 }, 172 python_tag = "py3", 173 # Requirements embedded into the wheel metadata. 174 requires = ["pytest"], 175 summary = "A one-line summary of this test package", 176 version = "0.0.1", 177 deps = [":example_pkg"], 178) 179 180# An example of how to change the wheel package root directory using 'strip_path_prefixes'. 181py_wheel( 182 name = "custom_package_root", 183 # Package data. We're building "examples_custom_package_root-0.0.1-py3-none-any.whl" 184 distribution = "examples_custom_package_root", 185 entry_points = { 186 "console_scripts": ["main = foo.bar:baz"], 187 }, 188 python_tag = "py3", 189 strip_path_prefixes = [ 190 "examples", 191 ], 192 version = "0.0.1", 193 deps = [ 194 ":example_pkg", 195 ], 196) 197 198py_wheel( 199 name = "custom_package_root_multi_prefix", 200 # Package data. We're building "custom_custom_package_root_multi_prefix-0.0.1-py3-none-any.whl" 201 distribution = "example_custom_package_root_multi_prefix", 202 python_tag = "py3", 203 strip_path_prefixes = [ 204 "examples/wheel/lib", 205 "examples/wheel", 206 ], 207 version = "0.0.1", 208 deps = [ 209 ":example_pkg", 210 ], 211) 212 213py_wheel( 214 name = "custom_package_root_multi_prefix_reverse_order", 215 # Package data. We're building "custom_custom_package_root_multi_prefix_reverse_order-0.0.1-py3-none-any.whl" 216 distribution = "example_custom_package_root_multi_prefix_reverse_order", 217 python_tag = "py3", 218 strip_path_prefixes = [ 219 "examples/wheel", 220 "examples/wheel/lib", # this is not effective, because the first prefix takes priority 221 ], 222 version = "0.0.1", 223 deps = [ 224 ":example_pkg", 225 ], 226) 227 228py_wheel( 229 name = "python_requires_in_a_package", 230 distribution = "example_python_requires_in_a_package", 231 python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", 232 python_tag = "py3", 233 version = "0.0.1", 234 deps = [ 235 ":example_pkg", 236 ], 237) 238 239py_wheel( 240 name = "use_rule_with_dir_in_outs", 241 distribution = "use_rule_with_dir_in_outs", 242 python_tag = "py3", 243 version = "0.0.1", 244 deps = [ 245 ":example_pkg_with_data", 246 ], 247) 248 249gen_python_config_settings() 250 251py_wheel( 252 name = "python_abi3_binary_wheel", 253 abi = "abi3", 254 distribution = "example_python_abi3_binary_wheel", 255 # these platform strings must line up with test_python_abi3_binary_wheel() in wheel_test.py 256 platform = select({ 257 ":aarch64-apple-darwin": "macosx_11_0_arm64", 258 ":aarch64-unknown-linux-gnu": "manylinux2014_aarch64", 259 ":x86_64-apple-darwin": "macosx_11_0_x86_64", # this is typically macosx_10_9_x86_64? 260 ":x86_64-pc-windows-msvc": "win_amd64", 261 ":x86_64-unknown-linux-gnu": "manylinux2014_x86_64", 262 }), 263 python_requires = ">=3.8", 264 python_tag = "cp38", 265 version = "0.0.1", 266) 267 268py_wheel( 269 name = "filename_escaping", 270 # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode 271 # runs of "-", "_" and "." should be replaced with a single underscore. 272 # Unicode non-ascii letters aren't allowed according to 273 # https://packaging.python.org/en/latest/specifications/name-normalization/. 274 distribution = "File--Name-Escaping", 275 python_tag = "py3", 276 version = "v0.0.1.RC1+ubuntu-r7", 277 deps = [":example_pkg"], 278) 279 280write_file( 281 name = "requires_file", 282 out = "requires.txt", 283 content = """\ 284# Requirements file 285--index-url https://pypi.com 286 287tomli>=2.0.0 288starlark # Example comment 289""".splitlines(), 290) 291 292write_file( 293 name = "extra_requires_file", 294 out = "extra_requires.txt", 295 content = """\ 296# Extras Requirements file 297--index-url https://pypi.com 298 299pyyaml>=6.0.0,!=6.0.1 300toml; (python_version == "3.11" or python_version == "3.12") and python_version != "3.8" 301wheel; python_version == "3.11" or python_version == "3.12" # Example comment 302""".splitlines(), 303) 304 305# py_wheel can use text files to specify their requirements. This 306# can be convenient for users of `compile_pip_requirements` who have 307# granular `requirements.in` files per package. This target shows 308# how to provide this file. 309py_wheel( 310 name = "requires_files", 311 distribution = "requires_files", 312 extra_requires_files = {":extra_requires.txt": "example"}, 313 python_tag = "py3", 314 # py_wheel can use text files to specify their requirements. This 315 # can be convenient for users of `compile_pip_requirements` who have 316 # granular `requirements.in` files per package. 317 requires_file = ":requires.txt", 318 version = "0.0.1", 319 deps = [":example_pkg"], 320) 321 322# Package just a specific py_libraries, without their dependencies 323py_wheel( 324 name = "minimal_data_files", 325 testonly = True, # Set this to verify the generated .dist target doesn't break things 326 327 # Re-using some files already checked into the repo. 328 data_files = { 329 "//examples/wheel:NOTICE": "scripts/NOTICE", 330 "README.md": "data/target/path/README.md", 331 }, 332 distribution = "minimal_data_files", 333 version = "0.0.1", 334) 335 336py_wheel( 337 name = "extra_requires", 338 distribution = "extra_requires", 339 extra_requires = {"example": [ 340 "pyyaml>=6.0.0,!=6.0.1", 341 'toml; (python_version == "3.11" or python_version == "3.12") and python_version != "3.8"', 342 'wheel; python_version == "3.11" or python_version == "3.12" ', 343 ]}, 344 python_tag = "py3", 345 # py_wheel can use text files to specify their requirements. This 346 # can be convenient for users of `compile_pip_requirements` who have 347 # granular `requirements.in` files per package. 348 requires = [ 349 "tomli>=2.0.0", 350 "starlark", 351 'pytest; python_version != "3.8"', 352 ], 353 version = "0.0.1", 354 deps = [":example_pkg"], 355) 356 357py_test( 358 name = "wheel_test", 359 srcs = ["wheel_test.py"], 360 data = [ 361 ":custom_package_root", 362 ":custom_package_root_multi_prefix", 363 ":custom_package_root_multi_prefix_reverse_order", 364 ":customized", 365 ":extra_requires", 366 ":filename_escaping", 367 ":minimal_data_files", 368 ":minimal_with_py_library", 369 ":minimal_with_py_library_with_stamp", 370 ":minimal_with_py_package", 371 ":python_abi3_binary_wheel", 372 ":python_requires_in_a_package", 373 ":requires_files", 374 ":use_rule_with_dir_in_outs", 375 ], 376 deps = [ 377 "//python/runfiles", 378 ], 379) 380 381# Test wheel publishing 382 383compile_pip_requirements( 384 name = "requirements_server", 385 src = "requirements_server.in", 386) 387 388py_test( 389 name = "test_publish", 390 srcs = ["test_publish.py"], 391 data = [ 392 ":minimal_with_py_library", 393 ":minimal_with_py_library.publish", 394 ":pypiserver", 395 ], 396 env = { 397 "PUBLISH_PATH": "$(location :minimal_with_py_library.publish)", 398 "SERVER_PATH": "$(location :pypiserver)", 399 "WHEEL_PATH": "$(rootpath :minimal_with_py_library)", 400 }, 401 target_compatible_with = select({ 402 "@platforms//os:linux": [], 403 "@platforms//os:macos": [], 404 "//conditions:default": ["@platforms//:incompatible"], 405 }), 406 deps = [ 407 "@pypiserver//pypiserver", 408 ], 409) 410 411py_console_script_binary( 412 name = "pypiserver", 413 pkg = "@pypiserver//pypiserver", 414 script = "pypi-server", 415) 416