1# Copyright 2021 The gRPC Authors 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"""Custom rules for gRPC Python""" 15 16# Adapted with modifications from 17# tensorflow/tensorflow/core/platform/default/build_config.bzl 18# Native Bazel rules don't exist yet to compile Cython code, but rules have 19# been written at cython/cython and tensorflow/tensorflow. We branch from 20# Tensorflow's version as it is more actively maintained and works for gRPC 21# Python's needs. 22def pyx_library(name, deps = [], py_deps = [], srcs = [], **kwargs): 23 """Compiles a group of .pyx / .pxd / .py files. 24 25 First runs Cython to create .cpp files for each input .pyx or .py + .pxd 26 pair. Then builds a shared object for each, passing "deps" to each cc_binary 27 rule (includes Python headers by default). Finally, creates a py_library rule 28 with the shared objects and any pure Python "srcs", with py_deps as its 29 dependencies; the shared objects can be imported like normal Python files. 30 31 Args: 32 name: Name for the rule. 33 deps: C/C++ dependencies of the Cython (e.g. Numpy headers). 34 py_deps: Pure Python dependencies of the final library. 35 srcs: .py, .pyx, or .pxd files to either compile or pass through. 36 **kwargs: Extra keyword arguments passed to the py_library. 37 """ 38 39 # First filter out files that should be run compiled vs. passed through. 40 py_srcs = [] 41 pyx_srcs = [] 42 pxd_srcs = [] 43 for src in srcs: 44 if src.endswith(".pyx") or (src.endswith(".py") and 45 src[:-3] + ".pxd" in srcs): 46 pyx_srcs.append(src) 47 elif src.endswith(".py"): 48 py_srcs.append(src) 49 else: 50 pxd_srcs.append(src) 51 if src.endswith("__init__.py"): 52 pxd_srcs.append(src) 53 54 # Invoke cython to produce the shared object libraries. 55 for filename in pyx_srcs: 56 native.genrule( 57 name = filename + "_cython_translation", 58 srcs = [filename], 59 outs = [filename.split(".")[0] + ".cpp"], 60 # Optionally use PYTHON_BIN_PATH on Linux platforms so that python 3 61 # works. Windows has issues with cython_binary so skip PYTHON_BIN_PATH. 62 cmd = 63 "PYTHONHASHSEED=0 $(location @cython//:cython_binary) --cplus $(SRCS) --output-file $(OUTS)", 64 tools = ["@cython//:cython_binary"] + pxd_srcs, 65 ) 66 67 shared_objects = [] 68 defines = kwargs.pop("defines", []) 69 for src in pyx_srcs: 70 stem = src.split(".")[0] 71 shared_object_name = stem + ".so" 72 native.cc_binary( 73 name = shared_object_name, 74 srcs = [stem + ".cpp"], 75 deps = deps + ["@local_config_python//:python_headers"], 76 defines = defines, 77 linkshared = 1, 78 ) 79 shared_objects.append(shared_object_name) 80 81 data = shared_objects[:] 82 data += kwargs.pop("data", []) 83 84 # Now create a py_library with these shared objects as data. 85 native.py_library( 86 name = name, 87 srcs = py_srcs, 88 deps = py_deps, 89 srcs_version = "PY2AND3", 90 data = data, 91 **kwargs 92 ) 93