python/setup.py (394 lines of code) (raw):

#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved. # # 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. # import os import platform import shutil import site import subprocess import sys import tempfile from distutils.cmd import Command from setuptools import Extension from setuptools import find_packages from setuptools import setup from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.develop import develop from setuptools.command.sdist import sdist from wheel.bdist_wheel import bdist_wheel try: import torch import torch.utils.cpp_extension except ImportError: torch = None # Enables --editable install with --user # https://github.com/pypa/pip/issues/7953 site.ENABLE_USER_SITE = "--user" in sys.argv[1:] pkg_root = os.path.dirname(os.path.abspath(__file__)) if platform.system() == "Darwin": # see also: https://github.com/python/cpython/issues/100420 if "arm" in platform.processor().lower(): os.environ["ARCHFLAGS"] = "-arch arm64" else: os.environ["ARCHFLAGS"] = "-arch x86_64" GL_EXT_NAME = "graphscope.learning.graphlearn.pywrap_graphlearn" GLTORCH_EXT_NAME = "graphscope.learning.graphlearn_torch.py_graphlearn_torch" GLTORCH_V6D_EXT_NAME = ( "graphscope.learning.graphlearn_torch.py_graphlearn_torch_vineyard" ) glt_root_path = os.path.abspath( os.path.join(pkg_root, "..", "learning_engine", "graphlearn-for-pytorch") ) class BuildProto(Command): description = "build protobuf file" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): cmd = [ sys.executable, os.path.join( pkg_root, "..", "proto", "proto_generator.py", ), os.path.join(pkg_root, "graphscope", "proto"), "--python", ] print(" ".join(cmd)) subprocess.check_call( cmd, env=os.environ.copy(), ) class GenerateFlexSDKDoc(Command): description = ( "generate flex client sdk documentation from openapi specification file" ) user_options = [] def initialize_options(self): self.with_doc = False def finalize_options(self): pass def run(self): # remove tempdir = os.path.join("/", tempfile.gettempprefix(), "flex_client") if os.path.exists(tempdir): shutil.rmtree(tempdir) # generate specification = os.path.join( pkg_root, "..", "flex", "openapi", "openapi_coordinator.yaml" ) cmd = [ "openapi-generator", "generate", "-g", "python", "-i", str(specification), "-o", str(tempdir), "--package-name", "graphscope.flex.rest", ] print(" ".join(cmd)) env = os.environ.copy() env["OPENAPI_GENERATOR_VERSION"] = "7.3.0" subprocess.check_call( cmd, env=env, ) targetdir = os.path.join( pkg_root, "..", "docs", "flex", "coordinator", "development", "python" ) subprocess.run( [ "sed", "-i", "s/# graphscope.flex.rest/# Coordinator Python SDK Reference/", os.path.join(tempdir, "README.md"), ] ) subprocess.run(["cp", os.path.join(tempdir, "README.md"), targetdir]) subprocess.run(["cp", "-r", os.path.join(tempdir, "docs"), targetdir]) class FormatAndLint(Command): description = "format and lint code" user_options = [] user_options = [("inplace=", "i", "Run code formatter and linter inplace")] def initialize_options(self): self.inplace = False def finalize_options(self): if self.inplace or self.inplace == "True" or self.inplace == "true": self.inplace = True else: self.inplace = False def run(self): if self.inplace: subprocess.check_call([sys.executable, "-m", "isort", "."], cwd=pkg_root) subprocess.check_call([sys.executable, "-m", "black", "."], cwd=pkg_root) subprocess.check_call([sys.executable, "-m", "flake8", "."], cwd=pkg_root) else: subprocess.check_call( [sys.executable, "-m", "isort", "--check", "--diff", "."], cwd=pkg_root ) subprocess.check_call( [sys.executable, "-m", "black", "--check", "--diff", "."], cwd=pkg_root ) subprocess.check_call([sys.executable, "-m", "flake8", "."], cwd=pkg_root) class CustomBuildPy(build_py): def run(self): self.run_command("build_proto") build_py.run(self) class BuildGLExt(build_ext): def run(self): self.extensions = [ext for ext in self.extensions if ext.name == GL_EXT_NAME] self.run_command("build_proto") build_ext.run(self) class BuildGLTorchExt(torch.utils.cpp_extension.BuildExtension if torch else build_ext): def run(self): if torch is None: print("Building graphlearn-torch extension requires pytorch") print("Set WITH_GLTORCH=OFF if you don't need it.") return self.extensions = [ ext for ext in self.extensions if ext.name in [GLTORCH_EXT_NAME, GLTORCH_V6D_EXT_NAME] ] torch.utils.cpp_extension.BuildExtension.run(self) def _get_gcc_use_cxx_abi(self): if hasattr(self, "_gcc_use_cxx_abi"): return self._gcc_use_cxx_abi build_dir = os.path.join(glt_root_path, "cmake-build") os.makedirs(build_dir, exist_ok=True) output = subprocess.run( [shutil.which("cmake"), ".."], cwd=build_dir, capture_output=True, text=True, ) import re match = re.search(r"GCC_USE_CXX11_ABI: (\d)", str(output)) if match: self._gcc_use_cxx_abi = match.group(1) else: return None return self._gcc_use_cxx_abi def _add_gnu_cpp_abi_flag(self, extension): gcc_use_cxx_abi = ( self._get_gcc_use_cxx_abi() if extension.name == GLTORCH_V6D_EXT_NAME else str(int(torch._C._GLIBCXX_USE_CXX11_ABI)) ) print(f"GCC_USE_CXX11_ABI for {extension.name}: {gcc_use_cxx_abi}") if gcc_use_cxx_abi is not None: self._add_compile_flag( extension, "-D_GLIBCXX_USE_CXX11_ABI=" + gcc_use_cxx_abi ) class CustomDevelop(develop): def run(self): develop.run(self) self.run_command("build_proto") class CustomSDist(sdist): def run(self): self.run_command("build_proto") sdist.run(self) class CustomBDistWheel(bdist_wheel): def finalize_options(self): bdist_wheel.finalize_options(self) self.root_is_pure = False def run(self): if sys.platform == "darwin": graphlearn_shared_lib = "libgraphlearn_shared.dylib" else: graphlearn_shared_lib = "libgraphlearn_shared.so" if os.environ.get("WITHOUT_LEARNING_ENGINE", None) is None: if not os.path.isfile( os.path.join( pkg_root, "..", "learning_engine", "graph-learn", "graphlearn", "built", "lib", graphlearn_shared_lib, ) ): raise ValueError("You must build the graphlearn library at first") self.run_command("build_proto") bdist_wheel.run(self) with open(os.path.join(pkg_root, "..", "README.md"), "r", encoding="utf-8") as fp: long_description = fp.read() def parsed_reqs(): with open(os.path.join(pkg_root, "requirements.txt"), "r", encoding="utf-8") as fp: return fp.read().splitlines() def parsed_dev_reqs(): with open( os.path.join(pkg_root, "requirements-dev.txt"), "r", encoding="utf-8" ) as fp: return fp.read().splitlines() def find_graphscope_packages(): packages = [] # add graphscope for pkg in find_packages( ".", exclude=["graphscope.flex.*", "graphscope.gsctl", "graphscope.gsctl.*"] ): packages.append(pkg) return packages def resolve_graphscope_package_dir(): package_dir = { "graphscope": "graphscope", } return package_dir def parsed_package_data(): return { "graphscope": [ "VERSION", "proto/*.pyi", ], } def build_learning_engine(): if os.environ.get("WITHOUT_LEARNING_ENGINE", None) is not None: return [] ext_modules = [graphlearn_ext()] if torch and os.path.exists(os.path.join(glt_root_path, "graphlearn_torch")): sys.path.insert( 0, os.path.join(glt_root_path, "graphlearn_torch", "python", "utils") ) from build_glt import glt_ext_module from build_glt import glt_v6d_ext_module ext_modules.append( glt_ext_module( name=GLTORCH_EXT_NAME, root_path=glt_root_path, with_cuda=False, release=False, ) ) ext_modules.append( glt_v6d_ext_module( name=GLTORCH_V6D_EXT_NAME, root_path=glt_root_path, ) ) sys.path.pop(0) return ext_modules def graphlearn_ext(): import numpy ROOT_PATH = os.path.abspath( os.path.join(pkg_root, "..", "learning_engine", "graph-learn") ) include_dirs = [] library_dirs = [] libraries = [] extra_compile_args = [] extra_link_args = [] if "GRAPHSCOPE_HOME" in os.environ: include_dirs.append(os.environ["GRAPHSCOPE_HOME"] + "/include") include_dirs.append("/usr/local/include") include_dirs.append(ROOT_PATH) include_dirs.append(ROOT_PATH + "/graphlearn") include_dirs.append(ROOT_PATH + "/graphlearn/src") include_dirs.append(ROOT_PATH + "/graphlearn/src/include") include_dirs.append(ROOT_PATH + "/graphlearn/built") include_dirs.append(ROOT_PATH + "/third_party/pybind11/pybind11/include") include_dirs.append(ROOT_PATH + "/third_party/glog/build") include_dirs.append(ROOT_PATH + "/third_party/protobuf/build/include") include_dirs.append(numpy.get_include()) library_dirs.append(ROOT_PATH + "/graphlearn/built/lib") extra_compile_args.append("-D__USE_XOPEN2K8") extra_compile_args.append("-std=c++17") extra_compile_args.append("-fvisibility=hidden") if sys.platform == "darwin": # mac M1 support include_dirs.append("/opt/homebrew/include") # explicitly link against protobuf to avoid the error # "illegal thread local variable reference to regular symbol" library_dirs.append("/usr/local/lib") library_dirs.append("/opt/homebrew/lib") libraries.append("protobuf") libraries.append("graphlearn_shared") if sys.platform == "linux" or sys.platform == "linux2": extra_link_args.append("-Wl,-rpath=$ORIGIN/built/lib") if sys.platform == "darwin": extra_link_args.append("-Wl,-rpath,@loader_path/built/lib") sources = [ ROOT_PATH + "/graphlearn/python/c/py_client.cc", ROOT_PATH + "/graphlearn/python/c/py_export.cc", # KNN not enabled # ROOT_PATH + "/graphlearn/python/c/py_contrib.cc", ] return Extension( GL_EXT_NAME, sources, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, ) def parse_version(root, **kwargs): """ Parse function for setuptools_scm that first tries to read '../VERSION' file to get a version number. """ from setuptools_scm.git import parse from setuptools_scm.version import meta version_file = os.path.join(pkg_root, "..", "VERSION") if os.path.isfile(version_file): with open(version_file, "r", encoding="utf-8") as fp: return meta(fp.read().strip()) return parse(root, **kwargs) setup( name="graphscope-client", description="GraphScope: A One-Stop Large-Scale Graph Computing System from Alibaba", long_description=long_description, long_description_content_type="text/markdown", author="Alibaba Damo Academy", author_email="graphscope@alibaba-inc.com", url="https://github.com/alibaba/GraphScope", license="Apache License 2.0", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: Apache Software License", "Topic :: Software Development :: Libraries", "Topic :: System :: Distributed Computing", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", ], keywords="Graph, Large-Scale, Distributed Computing", use_scm_version={ "root": pkg_root, "parse": parse_version, }, setup_requires=[ "setuptools_scm>=5.0.0,<8", ], package_dir=resolve_graphscope_package_dir(), packages=find_graphscope_packages(), package_data=parsed_package_data(), ext_modules=build_learning_engine(), cmdclass={ "build_ext": BuildGLExt, "build_gltorch_ext": BuildGLTorchExt, "build_proto": BuildProto, "build_py": CustomBuildPy, "generate_flex_sdk_doc": GenerateFlexSDKDoc, "bdist_wheel": CustomBDistWheel, "sdist": CustomSDist, "develop": CustomDevelop, "lint": FormatAndLint, }, install_requires=parsed_reqs(), extras_require={ "dev": parsed_dev_reqs(), }, project_urls={ "Documentation": "https://graphscope.io/docs", "Source": "https://github.com/alibaba/GraphScope", "Tracker": "https://github.com/alibaba/GraphScope/issues", }, ) if os.name == "nt": class _ReprableString(str): def __repr__(self) -> str: return self raise RuntimeError( _ReprableString( """ ==================================================================== GraphScope doesn't support Windows natively, please try to install graphscope in WSL https://docs.microsoft.com/en-us/windows/wsl/install with pip. ====================================================================""" ) )