xref: /aosp_15_r20/external/grpc-grpc/setup.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1# Copyright 2015 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"""A setup module for the GRPC Python package."""
15
16# NOTE(https://github.com/grpc/grpc/issues/24028): allow setuptools to monkey
17# patch distutils
18import setuptools  # isort:skip
19
20# Monkey Patch the unix compiler to accept ASM
21# files used by boring SSL.
22from distutils.unixccompiler import UnixCCompiler
23
24UnixCCompiler.src_extensions.append(".S")
25del UnixCCompiler
26
27import os
28import os.path
29import pathlib
30import platform
31import re
32import shlex
33import shutil
34import subprocess
35from subprocess import PIPE
36import sys
37import sysconfig
38
39import _metadata
40from setuptools import Extension
41from setuptools.command import egg_info
42
43# Redirect the manifest template from MANIFEST.in to PYTHON-MANIFEST.in.
44egg_info.manifest_maker.template = "PYTHON-MANIFEST.in"
45
46PY3 = sys.version_info.major == 3
47PYTHON_STEM = os.path.join("src", "python", "grpcio")
48CORE_INCLUDE = (
49    "include",
50    ".",
51)
52ABSL_INCLUDE = (os.path.join("third_party", "abseil-cpp"),)
53ADDRESS_SORTING_INCLUDE = (
54    os.path.join("third_party", "address_sorting", "include"),
55)
56CARES_INCLUDE = (
57    os.path.join("third_party", "cares", "cares", "include"),
58    os.path.join("third_party", "cares"),
59    os.path.join("third_party", "cares", "cares"),
60)
61if "darwin" in sys.platform:
62    CARES_INCLUDE += (os.path.join("third_party", "cares", "config_darwin"),)
63if "freebsd" in sys.platform:
64    CARES_INCLUDE += (os.path.join("third_party", "cares", "config_freebsd"),)
65if "linux" in sys.platform:
66    CARES_INCLUDE += (os.path.join("third_party", "cares", "config_linux"),)
67if "openbsd" in sys.platform:
68    CARES_INCLUDE += (os.path.join("third_party", "cares", "config_openbsd"),)
69RE2_INCLUDE = (os.path.join("third_party", "re2"),)
70SSL_INCLUDE = (
71    os.path.join("third_party", "boringssl-with-bazel", "src", "include"),
72)
73UPB_INCLUDE = (os.path.join("third_party", "upb"),)
74UPB_GRPC_GENERATED_INCLUDE = (os.path.join("src", "core", "ext", "upb-gen"),)
75UPBDEFS_GRPC_GENERATED_INCLUDE = (
76    os.path.join("src", "core", "ext", "upbdefs-gen"),
77)
78UTF8_RANGE_INCLUDE = (os.path.join("third_party", "utf8_range"),)
79XXHASH_INCLUDE = (os.path.join("third_party", "xxhash"),)
80ZLIB_INCLUDE = (os.path.join("third_party", "zlib"),)
81README = os.path.join(PYTHON_STEM, "README.rst")
82
83# Ensure we're in the proper directory whether or not we're being used by pip.
84os.chdir(os.path.dirname(os.path.abspath(__file__)))
85sys.path.insert(0, os.path.abspath(PYTHON_STEM))
86
87# Break import-style to ensure we can actually find our in-repo dependencies.
88import _parallel_compile_patch
89import _spawn_patch
90import grpc_core_dependencies
91
92import commands
93import grpc_version
94
95_parallel_compile_patch.monkeypatch_compile_maybe()
96_spawn_patch.monkeypatch_spawn()
97
98LICENSE = "Apache License 2.0"
99
100CLASSIFIERS = [
101    "Development Status :: 5 - Production/Stable",
102    "Programming Language :: Python",
103    "Programming Language :: Python :: 3",
104    "Programming Language :: Python :: 3.8",
105    "Programming Language :: Python :: 3.9",
106    "Programming Language :: Python :: 3.10",
107    "Programming Language :: Python :: 3.11",
108    "Programming Language :: Python :: 3.12",
109    "License :: OSI Approved :: Apache Software License",
110]
111
112
113def _env_bool_value(env_name, default):
114    """Parses a bool option from an environment variable"""
115    return os.environ.get(env_name, default).upper() not in ["FALSE", "0", ""]
116
117
118BUILD_WITH_BORING_SSL_ASM = _env_bool_value(
119    "GRPC_BUILD_WITH_BORING_SSL_ASM", "True"
120)
121
122# Export this environment variable to override the platform variant that will
123# be chosen for boringssl assembly optimizations. This option is useful when
124# crosscompiling and the host platform as obtained by sysconfig.get_platform()
125# doesn't match the platform we are targetting.
126# Example value: "linux-aarch64"
127BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM = os.environ.get(
128    "GRPC_BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM", ""
129)
130
131# Environment variable to determine whether or not the Cython extension should
132# *use* Cython or use the generated C files. Note that this requires the C files
133# to have been generated by building first *with* Cython support. Even if this
134# is set to false, if the script detects that the generated `.c` file isn't
135# present, then it will still attempt to use Cython.
136BUILD_WITH_CYTHON = _env_bool_value("GRPC_PYTHON_BUILD_WITH_CYTHON", "False")
137
138# Export this variable to use the system installation of openssl. You need to
139# have the header files installed (in /usr/include/openssl) and during
140# runtime, the shared library must be installed
141BUILD_WITH_SYSTEM_OPENSSL = _env_bool_value(
142    "GRPC_PYTHON_BUILD_SYSTEM_OPENSSL", "False"
143)
144
145# Export this variable to use the system installation of zlib. You need to
146# have the header files installed (in /usr/include/) and during
147# runtime, the shared library must be installed
148BUILD_WITH_SYSTEM_ZLIB = _env_bool_value(
149    "GRPC_PYTHON_BUILD_SYSTEM_ZLIB", "False"
150)
151
152# Export this variable to use the system installation of cares. You need to
153# have the header files installed (in /usr/include/) and during
154# runtime, the shared library must be installed
155BUILD_WITH_SYSTEM_CARES = _env_bool_value(
156    "GRPC_PYTHON_BUILD_SYSTEM_CARES", "False"
157)
158
159# Export this variable to use the system installation of re2. You need to
160# have the header files installed (in /usr/include/re2) and during
161# runtime, the shared library must be installed
162BUILD_WITH_SYSTEM_RE2 = _env_bool_value("GRPC_PYTHON_BUILD_SYSTEM_RE2", "False")
163
164# Export this variable to use the system installation of abseil. You need to
165# have the header files installed (in /usr/include/absl) and during
166# runtime, the shared library must be installed
167BUILD_WITH_SYSTEM_ABSL = os.environ.get("GRPC_PYTHON_BUILD_SYSTEM_ABSL", False)
168
169# Export this variable to force building the python extension with a statically linked libstdc++.
170# At least on linux, this is normally not needed as we can build manylinux-compatible wheels on linux just fine
171# without statically linking libstdc++ (which leads to a slight increase in the wheel size).
172# This option is useful when crosscompiling wheels for aarch64 where
173# it's difficult to ensure that the crosscompilation toolchain has a high-enough version
174# of GCC (we require >=5.1) but still uses old-enough libstdc++ symbols.
175# TODO(jtattermusch): remove this workaround once issues with crosscompiler version are resolved.
176BUILD_WITH_STATIC_LIBSTDCXX = _env_bool_value(
177    "GRPC_PYTHON_BUILD_WITH_STATIC_LIBSTDCXX", "False"
178)
179
180# For local development use only: This skips building gRPC Core and its
181# dependencies, including protobuf and boringssl. This allows "incremental"
182# compilation by first building gRPC Core using make, then building only the
183# Python/Cython layers here.
184#
185# Note that this requires libboringssl.a in the libs/{dbg,opt}/ directory, which
186# may require configuring make to not use the system openssl implementation:
187#
188#    make HAS_SYSTEM_OPENSSL_ALPN=0
189#
190# TODO(ericgribkoff) Respect the BUILD_WITH_SYSTEM_* flags alongside this option
191USE_PREBUILT_GRPC_CORE = _env_bool_value(
192    "GRPC_PYTHON_USE_PREBUILT_GRPC_CORE", "False"
193)
194
195# Environment variable to determine whether or not to enable coverage analysis
196# in Cython modules.
197ENABLE_CYTHON_TRACING = _env_bool_value(
198    "GRPC_PYTHON_ENABLE_CYTHON_TRACING", "False"
199)
200
201# Environment variable specifying whether or not there's interest in setting up
202# documentation building.
203ENABLE_DOCUMENTATION_BUILD = _env_bool_value(
204    "GRPC_PYTHON_ENABLE_DOCUMENTATION_BUILD", "False"
205)
206
207
208def check_linker_need_libatomic():
209    """Test if linker on system needs libatomic."""
210    code_test = (
211        b"#include <atomic>\n"
212        + b"int main() { return std::atomic<int64_t>{}; }"
213    )
214    cxx = shlex.split(os.environ.get("CXX", "c++"))
215    cpp_test = subprocess.Popen(
216        cxx + ["-x", "c++", "-std=c++14", "-"],
217        stdin=PIPE,
218        stdout=PIPE,
219        stderr=PIPE,
220    )
221    cpp_test.communicate(input=code_test)
222    if cpp_test.returncode == 0:
223        return False
224    # Double-check to see if -latomic actually can solve the problem.
225    # https://github.com/grpc/grpc/issues/22491
226    cpp_test = subprocess.Popen(
227        cxx + ["-x", "c++", "-std=c++14", "-", "-latomic"],
228        stdin=PIPE,
229        stdout=PIPE,
230        stderr=PIPE,
231    )
232    cpp_test.communicate(input=code_test)
233    return cpp_test.returncode == 0
234
235
236# There are some situations (like on Windows) where CC, CFLAGS, and LDFLAGS are
237# entirely ignored/dropped/forgotten by distutils and its Cygwin/MinGW support.
238# We use these environment variables to thus get around that without locking
239# ourselves in w.r.t. the multitude of operating systems this ought to build on.
240# We can also use these variables as a way to inject environment-specific
241# compiler/linker flags. We assume GCC-like compilers and/or MinGW as a
242# reasonable default.
243EXTRA_ENV_COMPILE_ARGS = os.environ.get("GRPC_PYTHON_CFLAGS", None)
244EXTRA_ENV_LINK_ARGS = os.environ.get("GRPC_PYTHON_LDFLAGS", None)
245if EXTRA_ENV_COMPILE_ARGS is None:
246    EXTRA_ENV_COMPILE_ARGS = ""
247    if "win32" in sys.platform:
248        # MSVC by defaults uses C++14 so C11 needs to be specified.
249        EXTRA_ENV_COMPILE_ARGS += " /std:c11"
250        # We need to statically link the C++ Runtime, only the C runtime is
251        # available dynamically
252        EXTRA_ENV_COMPILE_ARGS += " /MT"
253    elif "linux" in sys.platform:
254        # GCC by defaults uses C17 so only C++14 needs to be specified.
255        EXTRA_ENV_COMPILE_ARGS += " -std=c++14"
256        EXTRA_ENV_COMPILE_ARGS += (
257            " -fvisibility=hidden -fno-wrapv -fno-exceptions"
258        )
259    elif "darwin" in sys.platform:
260        # AppleClang by defaults uses C17 so only C++14 needs to be specified.
261        EXTRA_ENV_COMPILE_ARGS += " -std=c++14"
262        EXTRA_ENV_COMPILE_ARGS += (
263            " -stdlib=libc++ -fvisibility=hidden -fno-wrapv -fno-exceptions"
264            " -DHAVE_UNISTD_H"
265        )
266
267if EXTRA_ENV_LINK_ARGS is None:
268    EXTRA_ENV_LINK_ARGS = ""
269    if "linux" in sys.platform or "darwin" in sys.platform:
270        EXTRA_ENV_LINK_ARGS += " -lpthread"
271        if check_linker_need_libatomic():
272            EXTRA_ENV_LINK_ARGS += " -latomic"
273    if "linux" in sys.platform:
274        EXTRA_ENV_LINK_ARGS += " -static-libgcc"
275
276# Explicitly link Core Foundation framework for MacOS to ensure no symbol is
277# missing when compiled using package managers like Conda.
278if "darwin" in sys.platform:
279    EXTRA_ENV_LINK_ARGS += " -framework CoreFoundation"
280
281EXTRA_COMPILE_ARGS = shlex.split(EXTRA_ENV_COMPILE_ARGS)
282EXTRA_LINK_ARGS = shlex.split(EXTRA_ENV_LINK_ARGS)
283
284if BUILD_WITH_STATIC_LIBSTDCXX:
285    EXTRA_LINK_ARGS.append("-static-libstdc++")
286
287CYTHON_EXTENSION_PACKAGE_NAMES = ()
288
289CYTHON_EXTENSION_MODULE_NAMES = ("grpc._cython.cygrpc",)
290
291CYTHON_HELPER_C_FILES = ()
292
293CORE_C_FILES = tuple(grpc_core_dependencies.CORE_SOURCE_FILES)
294if "win32" in sys.platform:
295    CORE_C_FILES = filter(lambda x: "third_party/cares" not in x, CORE_C_FILES)
296
297if BUILD_WITH_SYSTEM_OPENSSL:
298    CORE_C_FILES = filter(
299        lambda x: "third_party/boringssl" not in x, CORE_C_FILES
300    )
301    CORE_C_FILES = filter(lambda x: "src/boringssl" not in x, CORE_C_FILES)
302    SSL_INCLUDE = (os.path.join("/usr", "include", "openssl"),)
303
304if BUILD_WITH_SYSTEM_ZLIB:
305    CORE_C_FILES = filter(lambda x: "third_party/zlib" not in x, CORE_C_FILES)
306    ZLIB_INCLUDE = (os.path.join("/usr", "include"),)
307
308if BUILD_WITH_SYSTEM_CARES:
309    CORE_C_FILES = filter(lambda x: "third_party/cares" not in x, CORE_C_FILES)
310    CARES_INCLUDE = (os.path.join("/usr", "include"),)
311
312if BUILD_WITH_SYSTEM_RE2:
313    CORE_C_FILES = filter(lambda x: "third_party/re2" not in x, CORE_C_FILES)
314    RE2_INCLUDE = (os.path.join("/usr", "include", "re2"),)
315
316if BUILD_WITH_SYSTEM_ABSL:
317    CORE_C_FILES = filter(
318        lambda x: "third_party/abseil-cpp" not in x, CORE_C_FILES
319    )
320    ABSL_INCLUDE = (os.path.join("/usr", "include"),)
321
322EXTENSION_INCLUDE_DIRECTORIES = (
323    (PYTHON_STEM,)
324    + CORE_INCLUDE
325    + ABSL_INCLUDE
326    + ADDRESS_SORTING_INCLUDE
327    + CARES_INCLUDE
328    + RE2_INCLUDE
329    + SSL_INCLUDE
330    + UPB_INCLUDE
331    + UPB_GRPC_GENERATED_INCLUDE
332    + UPBDEFS_GRPC_GENERATED_INCLUDE
333    + UTF8_RANGE_INCLUDE
334    + XXHASH_INCLUDE
335    + ZLIB_INCLUDE
336)
337
338EXTENSION_LIBRARIES = ()
339if "linux" in sys.platform:
340    EXTENSION_LIBRARIES += ("rt",)
341if not "win32" in sys.platform:
342    EXTENSION_LIBRARIES += ("m",)
343if "win32" in sys.platform:
344    EXTENSION_LIBRARIES += (
345        "advapi32",
346        "bcrypt",
347        "dbghelp",
348        "ws2_32",
349    )
350if BUILD_WITH_SYSTEM_OPENSSL:
351    EXTENSION_LIBRARIES += (
352        "ssl",
353        "crypto",
354    )
355if BUILD_WITH_SYSTEM_ZLIB:
356    EXTENSION_LIBRARIES += ("z",)
357if BUILD_WITH_SYSTEM_CARES:
358    EXTENSION_LIBRARIES += ("cares",)
359if BUILD_WITH_SYSTEM_RE2:
360    EXTENSION_LIBRARIES += ("re2",)
361if BUILD_WITH_SYSTEM_ABSL:
362    EXTENSION_LIBRARIES += tuple(
363        lib.stem[3:]
364        for lib in sorted(pathlib.Path("/usr").glob("lib*/libabsl_*.so"))
365    )
366
367DEFINE_MACROS = (("_WIN32_WINNT", 0x600),)
368asm_files = []
369
370
371# Quotes on Windows build macros are evaluated differently from other platforms,
372# so we must apply quotes asymmetrically in order to yield the proper result in
373# the binary.
374def _quote_build_define(argument):
375    if "win32" in sys.platform:
376        return '"\\"{}\\""'.format(argument)
377    return '"{}"'.format(argument)
378
379
380DEFINE_MACROS += (
381    ("GRPC_XDS_USER_AGENT_NAME_SUFFIX", _quote_build_define("Python")),
382    (
383        "GRPC_XDS_USER_AGENT_VERSION_SUFFIX",
384        _quote_build_define(_metadata.__version__),
385    ),
386)
387
388asm_key = ""
389if BUILD_WITH_BORING_SSL_ASM and not BUILD_WITH_SYSTEM_OPENSSL:
390    boringssl_asm_platform = (
391        BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM
392        if BUILD_OVERRIDE_BORING_SSL_ASM_PLATFORM
393        else sysconfig.get_platform()
394    )
395    if "i686" in boringssl_asm_platform:
396        print("Enabling SSE2 on %s platform" % boringssl_asm_platform)
397        EXTRA_COMPILE_ARGS.append("-msse2")
398    else:
399        print("SSE2 not enabled on %s platform" % boringssl_asm_platform)
400    # BoringSSL's gas-compatible assembly files are all internally conditioned
401    # by the preprocessor. Provided the platform has a gas-compatible assembler
402    # (i.e. not Windows), we can include the assembly files and let BoringSSL
403    # decide which ones should and shouldn't be used for the build.
404    if not boringssl_asm_platform.startswith("win"):
405        asm_key = "crypto_asm"
406    else:
407        print(
408            "ASM Builds for BoringSSL currently not supported on:",
409            boringssl_asm_platform,
410        )
411if asm_key:
412    asm_files = grpc_core_dependencies.ASM_SOURCE_FILES[asm_key]
413else:
414    DEFINE_MACROS += (("OPENSSL_NO_ASM", 1),)
415
416if "win32" in sys.platform:
417    # TODO(zyc): Re-enable c-ares on x64 and x86 windows after fixing the
418    # ares_library_init compilation issue
419    DEFINE_MACROS += (
420        ("WIN32_LEAN_AND_MEAN", 1),
421        ("CARES_STATICLIB", 1),
422        ("GRPC_ARES", 0),
423        ("NTDDI_VERSION", 0x06000000),
424        ("NOMINMAX", 1),
425    )
426    if "64bit" in platform.architecture()[0]:
427        DEFINE_MACROS += (("MS_WIN64", 1),)
428    elif sys.version_info >= (3, 5):
429        # For some reason, this is needed to get access to inet_pton/inet_ntop
430        # on msvc, but only for 32 bits
431        DEFINE_MACROS += (("NTDDI_VERSION", 0x06000000),)
432else:
433    DEFINE_MACROS += (
434        ("HAVE_CONFIG_H", 1),
435        ("GRPC_ENABLE_FORK_SUPPORT", 1),
436    )
437
438# Fix for multiprocessing support on Apple devices.
439# TODO(vigneshbabu): Remove this once the poll poller gets fork support.
440DEFINE_MACROS += (("GRPC_DO_NOT_INSTANTIATE_POSIX_POLLER", 1),)
441
442# Fix for Cython build issue in aarch64.
443# It's required to define this macro before include <inttypes.h>.
444# <inttypes.h> was included in core/lib/channel/call_tracer.h.
445# This macro should already be defined in grpc/grpc.h through port_platform.h,
446# but we're still having issue in aarch64, so we manually define the macro here.
447# TODO(xuanwn): Figure out what's going on in the aarch64 build so we can support
448# gcc + Bazel.
449DEFINE_MACROS += (("__STDC_FORMAT_MACROS", None),)
450
451LDFLAGS = tuple(EXTRA_LINK_ARGS)
452CFLAGS = tuple(EXTRA_COMPILE_ARGS)
453if "linux" in sys.platform or "darwin" in sys.platform:
454    pymodinit_type = "PyObject*" if PY3 else "void"
455    pymodinit = 'extern "C" __attribute__((visibility ("default"))) {}'.format(
456        pymodinit_type
457    )
458    DEFINE_MACROS += (("PyMODINIT_FUNC", pymodinit),)
459    DEFINE_MACROS += (("GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK", 1),)
460
461
462def cython_extensions_and_necessity():
463    cython_module_files = [
464        os.path.join(PYTHON_STEM, name.replace(".", "/") + ".pyx")
465        for name in CYTHON_EXTENSION_MODULE_NAMES
466    ]
467    config = os.environ.get("CONFIG", "opt")
468    prefix = "libs/" + config + "/"
469    if USE_PREBUILT_GRPC_CORE:
470        extra_objects = [
471            prefix + "libares.a",
472            prefix + "libboringssl.a",
473            prefix + "libgpr.a",
474            prefix + "libgrpc.a",
475        ]
476        core_c_files = []
477    else:
478        core_c_files = list(CORE_C_FILES)
479        extra_objects = []
480    extensions = [
481        Extension(
482            name=module_name,
483            sources=(
484                [module_file]
485                + list(CYTHON_HELPER_C_FILES)
486                + core_c_files
487                + asm_files
488            ),
489            include_dirs=list(EXTENSION_INCLUDE_DIRECTORIES),
490            libraries=list(EXTENSION_LIBRARIES),
491            define_macros=list(DEFINE_MACROS),
492            extra_objects=extra_objects,
493            extra_compile_args=list(CFLAGS),
494            extra_link_args=list(LDFLAGS),
495        )
496        for (module_name, module_file) in zip(
497            list(CYTHON_EXTENSION_MODULE_NAMES), cython_module_files
498        )
499    ]
500    need_cython = BUILD_WITH_CYTHON
501    if not BUILD_WITH_CYTHON:
502        need_cython = (
503            need_cython
504            or not commands.check_and_update_cythonization(extensions)
505        )
506    # TODO: the strategy for conditional compiling and exposing the aio Cython
507    # dependencies will be revisited by https://github.com/grpc/grpc/issues/19728
508    return (
509        commands.try_cythonize(
510            extensions,
511            linetracing=ENABLE_CYTHON_TRACING,
512            mandatory=BUILD_WITH_CYTHON,
513        ),
514        need_cython,
515    )
516
517
518CYTHON_EXTENSION_MODULES, need_cython = cython_extensions_and_necessity()
519
520PACKAGE_DIRECTORIES = {
521    "": PYTHON_STEM,
522}
523
524INSTALL_REQUIRES = ()
525
526EXTRAS_REQUIRES = {
527    "protobuf": "grpcio-tools>={version}".format(version=grpc_version.VERSION),
528}
529
530SETUP_REQUIRES = (
531    INSTALL_REQUIRES + ("Sphinx~=1.8.1",) if ENABLE_DOCUMENTATION_BUILD else ()
532)
533
534try:
535    import Cython
536except ImportError:
537    if BUILD_WITH_CYTHON:
538        sys.stderr.write(
539            "You requested a Cython build via GRPC_PYTHON_BUILD_WITH_CYTHON, "
540            "but do not have Cython installed. We won't stop you from using "
541            "other commands, but the extension files will fail to build.\n"
542        )
543    elif need_cython:
544        sys.stderr.write(
545            "We could not find Cython. Setup may take 10-20 minutes.\n"
546        )
547        SETUP_REQUIRES += ("cython>=3.0.0",)
548
549COMMAND_CLASS = {
550    "doc": commands.SphinxDocumentation,
551    "build_project_metadata": commands.BuildProjectMetadata,
552    "build_py": commands.BuildPy,
553    "build_ext": commands.BuildExt,
554    "gather": commands.Gather,
555    "clean": commands.Clean,
556}
557
558# Ensure that package data is copied over before any commands have been run:
559credentials_dir = os.path.join(PYTHON_STEM, "grpc", "_cython", "_credentials")
560try:
561    os.mkdir(credentials_dir)
562except OSError:
563    pass
564shutil.copyfile(
565    os.path.join("etc", "roots.pem"), os.path.join(credentials_dir, "roots.pem")
566)
567
568PACKAGE_DATA = {
569    # Binaries that may or may not be present in the final installation, but are
570    # mentioned here for completeness.
571    "grpc._cython": [
572        "_credentials/roots.pem",
573        "_windows/grpc_c.32.python",
574        "_windows/grpc_c.64.python",
575    ],
576}
577PACKAGES = setuptools.find_packages(PYTHON_STEM)
578
579setuptools.setup(
580    name="grpcio",
581    version=grpc_version.VERSION,
582    description="HTTP/2-based RPC framework",
583    author="The gRPC Authors",
584    author_email="[email protected]",
585    url="https://grpc.io",
586    project_urls={
587        "Source Code": "https://github.com/grpc/grpc",
588        "Bug Tracker": "https://github.com/grpc/grpc/issues",
589        "Documentation": "https://grpc.github.io/grpc/python",
590    },
591    license=LICENSE,
592    classifiers=CLASSIFIERS,
593    long_description_content_type="text/x-rst",
594    long_description=open(README).read(),
595    ext_modules=CYTHON_EXTENSION_MODULES,
596    packages=list(PACKAGES),
597    package_dir=PACKAGE_DIRECTORIES,
598    package_data=PACKAGE_DATA,
599    python_requires=">=3.8",
600    install_requires=INSTALL_REQUIRES,
601    extras_require=EXTRAS_REQUIRES,
602    setup_requires=SETUP_REQUIRES,
603    cmdclass=COMMAND_CLASS,
604)
605