coordinator/setup.py (352 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 contextlib import glob import itertools import os import platform import shutil import subprocess import sys import tempfile from distutils.cmd import Command from setuptools import find_packages from setuptools import setup from setuptools.command.build_py import build_py from setuptools.command.develop import develop from setuptools.command.sdist import sdist def get_version(file): """Get the version of the package from the given file.""" __version__ = "" if os.path.isfile(file): with open(file, "r", encoding="utf-8") as fp: __version__ = fp.read().strip() return __version__ repo_root = os.path.dirname(os.path.abspath(__file__)) version = get_version(os.path.join(repo_root, "..", "VERSION")) GRAPHSCOPE_HOME = os.environ.get("GRAPHSCOPE_HOME", "/usr/local") INSTALL_PREFIX = os.environ.get("INSTALL_PREFIX", "/opt/graphscope") # copy any files contains in ${INSTALL_PREFIX} into site-packages/graphscope.runtime def _get_extra_data(): # noqa: C901 # Copy # 1) ${INSTALL_PREFIX} # 2) headers of arrow/glog/gflags/google/openmpi/vineyard # 3) openmpi # 4) builtin app libraries # into site-packages/graphscope.runtime # # For shrink the package size less than "100M", we split graphscope into # 1) graphscope: libs include *.so, runtime such as 'bin', and full-openmpi # 2) gs-coordinator: include python related code of gscoordinator # 3) gs-include: header files # 4) gs-engine: other runtime info such as 'conf', and *.jar # 5) gs-apps: precompiled builtin applications def __unknown_platform(action, platform): raise RuntimeError(f"Unknown platform '{platform}' to {action}") def __get_homebrew_prefix(package=None): cmd = [shutil.which("brew"), "--prefix"] if package is not None: cmd.append(package) return subprocess.check_output(cmd).decode("utf-8", errors="ignore").strip("\n") def __get_openmpi_prefix(): if platform.system() == "Linux": # install "/opt/openmpi" in gsruntime image return "/opt/openmpi" elif platform.system() == "Darwin": return __get_homebrew_prefix("openmpi") else: __unknown_platform("find openmpi", platform.system()) def __get_vineyard_prefix(): if platform.system() == "Linux": return GRAPHSCOPE_HOME elif platform.system() == "Darwin": return __get_homebrew_prefix("vineyard") else: __unknown_platform("find vineyard", platform.system()) def __get_lib_suffix(): if platform.system() == "Linux": return "so" elif platform.system() == "Darwin": return "dylib" else: __unknown_platform("resolve lib suffix", platform.system()) name = os.environ.get("package_name", "gs-coordinator") RUNTIME_ROOT = "graphscope.runtime" data = {} # data format: # {"source_dir": "package_dir"} or # {"source_dir": (package_dir, [exclude_list])} if name == "graphscope": # engine and lib data = { f"{INSTALL_PREFIX}/bin/": os.path.join(RUNTIME_ROOT, "bin"), f"{INSTALL_PREFIX}/lib/": os.path.join(RUNTIME_ROOT, "lib"), f"{INSTALL_PREFIX}/lib64/": os.path.join(RUNTIME_ROOT, "lib64"), f"{__get_vineyard_prefix()}/lib/libvineyard_internal_registry.{__get_lib_suffix()}": os.path.join( RUNTIME_ROOT, "lib" ), } # openmpi data.update( { __get_openmpi_prefix(): os.path.join(RUNTIME_ROOT), } ) elif name == "gs-engine": data = { f"{INSTALL_PREFIX}/conf/": os.path.join(RUNTIME_ROOT, "conf"), f"{INSTALL_PREFIX}/*.jar": os.path.join(RUNTIME_ROOT), } elif name == "gs-include": data = { f"{INSTALL_PREFIX}/include/": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/grape": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/string_view": os.path.join( RUNTIME_ROOT, "include" ), f"{__get_vineyard_prefix()}/include/vineyard": os.path.join( RUNTIME_ROOT, "include" ), f"{GRAPHSCOPE_HOME}/include/arrow": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/boost": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/glog": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/gflags": os.path.join(RUNTIME_ROOT, "include"), f"{GRAPHSCOPE_HOME}/include/google": os.path.join(RUNTIME_ROOT, "include"), } if platform.system() == "Linux": data["/usr/include/rapidjson"] = os.path.join(RUNTIME_ROOT, "include") data["/usr/include/msgpack"] = os.path.join(RUNTIME_ROOT, "include") data["/usr/include/msgpack.hpp"] = os.path.join(RUNTIME_ROOT, "include") # recent protobuf requires include absl directly if os.path.isdir(f"{GRAPHSCOPE_HOME}/include/absl"): data[f"{GRAPHSCOPE_HOME}/include/absl"] = os.path.join( RUNTIME_ROOT, "include" ) elif platform.system() == "Darwin": homebrew_prefix = __get_homebrew_prefix() data[f"{homebrew_prefix}/include/rapidjson"] = os.path.join( RUNTIME_ROOT, "include" ) data[f"{homebrew_prefix}/include/msgpack"] = os.path.join( RUNTIME_ROOT, "include" ) data[f"{homebrew_prefix}/include/msgpack.hpp"] = os.path.join( RUNTIME_ROOT, "include" ) # recent protobuf requires include absl directly if os.path.isdir(f"{homebrew_prefix}/include/absl"): data[f"{homebrew_prefix}/include/absl"] = os.path.join( RUNTIME_ROOT, "include" ) elif name == "gs-apps": # precompiled applications data = { os.path.join("/", tempfile.gettempprefix(), "gs", "builtin"): os.path.join( RUNTIME_ROOT, "precompiled" ), } return data class BuildBuiltin(Command): description = "Build builtin app gar file" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): app_home_path = os.path.join(repo_root, "gscoordinator", "builtin", "app") gar_file = os.path.join(app_home_path, "builtin_app.gar") zip_file = os.path.join(app_home_path, "builtin_app.zip") # Remove previous files if exist. for f in [gar_file, zip_file]: with contextlib.suppress(FileNotFoundError): os.remove(f) shutil.make_archive( os.path.join(app_home_path, "builtin_app"), format="zip", root_dir=app_home_path, base_dir=None, ) shutil.move( src=zip_file, dst=gar_file, ) 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=repo_root) subprocess.check_call([sys.executable, "-m", "black", "."], cwd=repo_root) subprocess.check_call([sys.executable, "-m", "flake8", "."], cwd=repo_root) else: subprocess.check_call( [sys.executable, "-m", "isort", "--check", "--diff", "."], cwd=repo_root ) subprocess.check_call( [sys.executable, "-m", "black", "--check", "--diff", "."], cwd=repo_root ) subprocess.check_call([sys.executable, "-m", "flake8", "."], cwd=repo_root) class CustomBuildPy(build_py): def _get_data_files(self): """Add custom out-of-tree package data files.""" rs = super()._get_data_files() if os.environ.get("WITH_EXTRA_DATA") != "ON": return rs for sources, package in _get_extra_data().items(): excludes = [] if isinstance(package, tuple): excludes = package[1] package = package[0] src_dir = os.path.dirname(sources) build_dir = os.path.join(*([self.build_lib] + package.split(os.sep))) filenames = [] for file in itertools.chain( glob.glob(sources + "/**/*", recursive=True), glob.glob(sources, recursive=False), ): if os.path.isfile(file) or ( os.path.islink(file) and not os.path.isdir(file) ): if not any([f in file for f in excludes]): filenames.append(os.path.relpath(file, src_dir)) rs.append((package, src_dir, build_dir, filenames)) return rs def run(self): self.run_command("build_builtin") build_py.run(self) class CustomDevelop(develop): def run(self): self.run_command("build_builtin") develop.run(self) class CustomSDist(sdist): def run(self): self.run_command("build_builtin") sdist.run(self) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "README.md"), encoding="utf-8", mode="r", ) as fp: long_description = fp.read() def parsed_package_dir(): name = os.environ.get("package_name", "gs-coordinator") if name == "gs-coordinator": return {".": "."} return {} def parsed_packages(): name = os.environ.get("package_name", "gs-coordinator") if name == "gs-coordinator": return find_packages(".") return ["graphscope_runtime"] def parsed_package_data(): name = os.environ.get("package_name", "gs-coordinator") if name == "gs-coordinator": return { "gscoordinator": [ "builtin/app/builtin_app.gar", "builtin/app/*.yaml", "template/*.template", "VERSION", ], } return {} def parsed_reqs(): name = os.environ.get("package_name", "gs-coordinator") if name == "gs-coordinator": with open( os.path.join(repo_root, "requirements.txt"), "r", encoding="utf-8" ) as fp: pkgs = fp.read().splitlines() pkgs.append(f"graphscope_client == {version}") return pkgs elif name == "graphscope": return [ f"gs-coordinator == {version}", f"gs-engine == {version}", f"gs-include == {version}", f"gs-apps == {version}", ] else: return [] def parsed_dev_reqs(): name = os.environ.get("package_name", "gs-coordinator") if name == "gs-coordinator": with open( os.path.join(repo_root, "requirements-dev.txt"), "r", encoding="utf-8" ) as fp: return {"dev": fp.read().splitlines()} return {} 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(repo_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) class GenerateFlexServer(Command): description = "generate flex server from OpenApi specification file" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): # generate specification = os.path.join( repo_root, "..", "flex", "openapi", "openapi_coordinator.yaml" ) cmd = [ "openapi-generator", "generate", "-g", "python-flask", "-i", str(specification), "-o", repo_root, "--package-name", "gscoordinator.flex", ] env = os.environ.copy() env["OPENAPI_GENERATOR_VERSION"] = "7.3.0" subprocess.check_call( cmd, env=env, ) setup( name=os.environ.get("package_name", "gs-coordinator"), description="", include_package_data=True, long_description=long_description, long_description_content_type="text/markdown", author="GraphScope Team, 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", "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="GraphScope, Graph Computations", use_scm_version={ "root": repo_root, "parse": parse_version, }, setup_requires=[ "setuptools_scm>=5.0.0,<8", ], package_dir=parsed_package_dir(), packages=parsed_packages(), package_data=parsed_package_data(), cmdclass={ "build_builtin": BuildBuiltin, "build_py": CustomBuildPy, "sdist": CustomSDist, "develop": CustomDevelop, "lint": FormatAndLint, "generate_flex_server": GenerateFlexServer, }, install_requires=parsed_reqs(), extras_require=parsed_dev_reqs(), ) if os.name == "nt": class _ReprableString(str): def __repr__(self) -> str: # pylint: disable=invalid-repr-returned 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. ====================================================================""" ) )