# Copyright 2021 The gRPC Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Custom rules for gRPC Python""" # Adapted with modifications from # tensorflow/tensorflow/core/platform/default/build_config.bzl # Native Bazel rules don't exist yet to compile Cython code, but rules have # been written at cython/cython and tensorflow/tensorflow. We branch from # Tensorflow's version as it is more actively maintained and works for gRPC # Python's needs. def pyx_library(name, deps = [], py_deps = [], srcs = [], **kwargs): """Compiles a group of .pyx / .pxd / .py files. First runs Cython to create .cpp files for each input .pyx or .py + .pxd pair. Then builds a shared object for each, passing "deps" to each cc_binary rule (includes Python headers by default). Finally, creates a py_library rule with the shared objects and any pure Python "srcs", with py_deps as its dependencies; the shared objects can be imported like normal Python files. Args: name: Name for the rule. deps: C/C++ dependencies of the Cython (e.g. Numpy headers). py_deps: Pure Python dependencies of the final library. srcs: .py, .pyx, or .pxd files to either compile or pass through. **kwargs: Extra keyword arguments passed to the py_library. """ # First filter out files that should be run compiled vs. passed through. py_srcs = [] pyx_srcs = [] pxd_srcs = [] for src in srcs: if src.endswith(".pyx") or (src.endswith(".py") and src[:-3] + ".pxd" in srcs): pyx_srcs.append(src) elif src.endswith(".py"): py_srcs.append(src) else: pxd_srcs.append(src) if src.endswith("__init__.py"): pxd_srcs.append(src) # Invoke cython to produce the shared object libraries. for filename in pyx_srcs: native.genrule( name = filename + "_cython_translation", srcs = [filename], outs = [filename.split(".")[0] + ".cpp"], # Optionally use PYTHON_BIN_PATH on Linux platforms so that python 3 # works. Windows has issues with cython_binary so skip PYTHON_BIN_PATH. cmd = "PYTHONHASHSEED=0 $(location @cython//:cython_binary) --cplus $(SRCS) --output-file $(OUTS)", tools = ["@cython//:cython_binary"] + pxd_srcs, ) shared_objects = [] defines = kwargs.pop("defines", []) for src in pyx_srcs: stem = src.split(".")[0] shared_object_name = stem + ".so" native.cc_binary( name = shared_object_name, srcs = [stem + ".cpp"], deps = deps + ["@local_config_python//:python_headers"], defines = defines, linkshared = 1, ) shared_objects.append(shared_object_name) data = shared_objects[:] data += kwargs.pop("data", []) # Now create a py_library with these shared objects as data. native.py_library( name = name, srcs = py_srcs, deps = py_deps, srcs_version = "PY2AND3", data = data, **kwargs )