setup.py 6.79 KB
Newer Older
Minjie Wang's avatar
Minjie Wang committed
1
2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
3
import glob
4
import os
Minjie Wang's avatar
Minjie Wang committed
5
import shutil
6
7
import sys
import sysconfig
Minjie Wang's avatar
Minjie Wang committed
8

9
from setuptools import find_packages, setup
Minjie Wang's avatar
Minjie Wang committed
10
from setuptools.dist import Distribution
11
from setuptools.extension import Extension
Minjie Wang's avatar
Minjie Wang committed
12

13

Minjie Wang's avatar
Minjie Wang committed
14
15
class BinaryDistribution(Distribution):
    def has_ext_modules(self):
16
        return True
Minjie Wang's avatar
Minjie Wang committed
17

18

Minjie Wang's avatar
Minjie Wang committed
19
20
CURRENT_DIR = os.path.dirname(__file__)

21

Minjie Wang's avatar
Minjie Wang committed
22
23
def get_lib_path():
    """Get library path, name and version"""
24
    # We can not import `libinfo.py` in setup.py directly since __init__.py
Minjie Wang's avatar
Minjie Wang committed
25
    # Will be invoked which introduces dependences
26
27
    libinfo_py = os.path.join(CURRENT_DIR, "./dgl/_ffi/libinfo.py")
    libinfo = {"__file__": libinfo_py}
28
    exec(
29
30
        compile(open(libinfo_py, "rb").read(), libinfo_py, "exec"),
        libinfo,
31
        libinfo,
32
33
    )
    version = libinfo["__version__"]
Gan Quan's avatar
Gan Quan committed
34

35
    lib_path = libinfo["find_lib_path"]()
Gan Quan's avatar
Gan Quan committed
36
37
    libs = [lib_path[0]]

Minjie Wang's avatar
Minjie Wang committed
38
39
    return libs, version

40

peizhou001's avatar
peizhou001 committed
41
def get_lib_pattern(lib_name):
42
    if sys.platform.startswith("linux"):
peizhou001's avatar
peizhou001 committed
43
        lib_pattern = f"lib{lib_name}_*.so"
44
    elif sys.platform.startswith("darwin"):
peizhou001's avatar
peizhou001 committed
45
        lib_pattern = f"lib{lib_name}_*.dylib"
46
    elif sys.platform.startswith("win"):
peizhou001's avatar
peizhou001 committed
47
        lib_pattern = f"{lib_name}_*.dll"
48
    else:
49
        raise NotImplementedError("Unsupported system: %s" % sys.platform)
peizhou001's avatar
peizhou001 committed
50
    return lib_pattern
51
52


Minjie Wang's avatar
Minjie Wang committed
53
LIBS, VERSION = get_lib_path()
54
BACKENDS = ["pytorch"]
peizhou001's avatar
peizhou001 committed
55
56
57
58
59
60
61
62
63
64


def remove_lib(lib_name):
    for lib_path in glob.glob(
        os.path.join(CURRENT_DIR, "dgl", lib_name, get_lib_pattern(lib_name))
    ):
        try:
            os.remove(lib_path)
        except BaseException:
            pass
65

66

67
68
69
70
def cleanup():
    # Wheel cleanup
    try:
        os.remove("MANIFEST.in")
71
    except BaseException:
72
73
74
75
76
77
        pass

    for path in LIBS:
        _, libname = os.path.split(path)
        try:
            os.remove(os.path.join("dgl", libname))
78
        except BaseException:
79
80
            pass
    for backend in BACKENDS:
peizhou001's avatar
peizhou001 committed
81
        remove_lib("tensoradapter")
Minjie Wang's avatar
Minjie Wang committed
82

83
        if backend == "pytorch":
peizhou001's avatar
peizhou001 committed
84
85
            remove_lib("dgl_sparse")
            remove_lib("graphbolt")
86

87

88
89
def config_cython():
    """Try to configure cython and return cython configuration"""
90
91
92
93
    if sys.platform.startswith("win"):
        print(
            "WARNING: Cython is not supported on Windows, will compile without cython module"
        )
94
95
96
97
        return []
    sys_cflags = sysconfig.get_config_var("CFLAGS")

    if "i386" in sys_cflags and "x86_64" in sys_cflags:
98
        print(
99
100
            "WARNING: Cython library may not be compiled correctly with both i386 and x64"
        )
101
102
103
        return []
    try:
        from Cython.Build import cythonize
104

105
106
107
108
109
110
111
        # from setuptools.extension import Extension
        if sys.version_info >= (3, 0):
            subdir = "_cy3"
        else:
            subdir = "_cy2"
        ret = []
        path = "dgl/_ffi/_cython"
112
113
        library_dirs = ["dgl", "../build/Release", "../build"]
        libraries = ["dgl"]
114
115
116
        for fn in os.listdir(path):
            if not fn.endswith(".pyx"):
                continue
117
118
119
120
121
122
123
124
125
126
127
128
            ret.append(
                Extension(
                    "dgl._ffi.%s.%s" % (subdir, fn[:-4]),
                    ["dgl/_ffi/_cython/%s" % fn],
                    include_dirs=[
                        "../include/",
                        "../third_party/dmlc-core/include",
                        "../third_party/dlpack/include",
                    ],
                    library_dirs=library_dirs,
                    libraries=libraries,
                    # Crashes without this flag with GCC 5.3.1
129
                    extra_compile_args=["-std=c++17"],
130
131
132
                    language="c++",
                )
            )
Xin Yao's avatar
Xin Yao committed
133
134
135
        return cythonize(
            ret, force=True, compiler_directives={"language_level": "3"}
        )
136
    except ImportError:
137
138
139
        print(
            "WARNING: Cython is not installed, will compile without cython module"
        )
140
141
        return []

142

peizhou001's avatar
peizhou001 committed
143
144
145
146
147
def copy_lib(lib_name, backend=""):
    for lib_path in glob.glob(
        os.path.join(dir_, lib_name, backend, get_lib_pattern(lib_name))
    ):
        lib_file_name = os.path.basename(lib_path)
Xin Yao's avatar
Xin Yao committed
148
        dst_dir_ = os.path.join(CURRENT_DIR, "dgl", lib_name, backend)
peizhou001's avatar
peizhou001 committed
149
150
151
152
153
154
155
156
        os.makedirs(
            dst_dir_,
            exist_ok=True,
        )
        shutil.copy(
            os.path.join(dir_, lib_name, backend, lib_file_name),
            dst_dir_,
        )
Xin Yao's avatar
Xin Yao committed
157
        fo.write(f"include dgl/{lib_name}/{backend}/{lib_file_name}\n")
peizhou001's avatar
peizhou001 committed
158
159


Minjie Wang's avatar
Minjie Wang committed
160
161
include_libs = False
wheel_include_libs = False
162
if "bdist_wheel" in sys.argv or os.getenv("CONDA_BUILD"):
Gan Quan's avatar
Gan Quan committed
163
    wheel_include_libs = True
164
165
elif "clean" in sys.argv:
    cleanup()
Gan Quan's avatar
Gan Quan committed
166
167
else:
    include_libs = True
Minjie Wang's avatar
Minjie Wang committed
168
169
170
171
172
173
174

setup_kwargs = {}

# For bdist_wheel only
if wheel_include_libs:
    with open("MANIFEST.in", "w") as fo:
        for path in LIBS:
175
            shutil.copy(path, os.path.join(CURRENT_DIR, "dgl"))
176
            dir_, libname = os.path.split(path)
Minjie Wang's avatar
Minjie Wang committed
177
            fo.write("include dgl/%s\n" % libname)
178
179

        for backend in BACKENDS:
peizhou001's avatar
peizhou001 committed
180
            copy_lib("tensoradapter", backend)
181
            if backend == "pytorch":
peizhou001's avatar
peizhou001 committed
182
183
                copy_lib("dgl_sparse")
                copy_lib("graphbolt")
184
    setup_kwargs = {"include_package_data": True}
Minjie Wang's avatar
Minjie Wang committed
185

peizhou001's avatar
peizhou001 committed
186
187
188

def get_lib_file_path(lib_name, backend=""):
    return (
Xin Yao's avatar
Xin Yao committed
189
        f"dgl/{lib_name}/{backend}",
peizhou001's avatar
peizhou001 committed
190
191
192
193
194
195
196
197
198
199
200
        glob.glob(
            os.path.join(
                os.path.dirname(os.path.relpath(path, CURRENT_DIR)),
                lib_name,
                backend,
                get_lib_pattern(lib_name),
            )
        ),
    )


Minjie Wang's avatar
Minjie Wang committed
201
# For source tree setup
Gan Quan's avatar
Gan Quan committed
202
# Conda build also includes the binary library
Minjie Wang's avatar
Minjie Wang committed
203
204
if include_libs:
    rpath = [os.path.relpath(path, CURRENT_DIR) for path in LIBS]
205
    data_files = [("dgl", rpath)]
206
207
    for path in LIBS:
        for backend in BACKENDS:
peizhou001's avatar
peizhou001 committed
208
            data_files.append(get_lib_file_path("tensoradapter", backend))
209
            if backend == "pytorch":
peizhou001's avatar
peizhou001 committed
210
211
                data_files.append(get_lib_file_path("dgl_sparse"))
                data_files.append(get_lib_file_path("graphbolt"))
212
    setup_kwargs = {"include_package_data": True, "data_files": data_files}
Minjie Wang's avatar
Minjie Wang committed
213
214

setup(
215
    name="dgl" + os.getenv("DGL_PACKAGE_SUFFIX", ""),
Minjie Wang's avatar
Minjie Wang committed
216
    version=VERSION,
217
    description="Deep Graph Library",
Minjie Wang's avatar
Minjie Wang committed
218
    zip_safe=False,
219
220
    maintainer="DGL Team",
    maintainer_email="wmjlyjemaine@gmail.com",
Minjie Wang's avatar
Minjie Wang committed
221
    packages=find_packages(),
Minjie Wang's avatar
Minjie Wang committed
222
    install_requires=[
223
224
225
226
227
228
        "numpy>=1.14.0",
        "scipy>=1.1.0",
        "networkx>=2.1",
        "requests>=2.19.0",
        "tqdm",
        "psutil>=5.8.0",
229
        "torchdata",
Minjie Wang's avatar
Minjie Wang committed
230
    ],
231
    url="https://github.com/dmlc/dgl",
Minjie Wang's avatar
Minjie Wang committed
232
    distclass=BinaryDistribution,
233
    ext_modules=config_cython(),
Minjie Wang's avatar
Minjie Wang committed
234
    classifiers=[
235
236
237
        "Development Status :: 3 - Alpha",
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: Apache Software License",
Minjie Wang's avatar
Minjie Wang committed
238
    ],
239
    license="APACHE",
peizhou001's avatar
peizhou001 committed
240
    **setup_kwargs,
Minjie Wang's avatar
Minjie Wang committed
241
242
243
)

if wheel_include_libs:
244
    cleanup()