Caffe2 - Python API
A deep learning, cross platform ML framework
build_pytorch_libs.py
1 from .setup_helpers.env import (IS_64BIT, IS_ARM, IS_DARWIN, IS_LINUX, IS_PPC, IS_WINDOWS,
2  DEBUG, REL_WITH_DEB_INFO, USE_MKLDNN,
3  check_env_flag, check_negative_env_flag, hotpatch_build_env_vars)
4 
5 import os
6 import sys
7 import distutils
8 import distutils.sysconfig
9 from distutils.file_util import copy_file
10 from distutils.dir_util import copy_tree
11 from subprocess import check_call, call, check_output
12 from distutils.version import LooseVersion
13 from .setup_helpers.cuda import USE_CUDA, CUDA_HOME
14 from .setup_helpers.dist_check import USE_DISTRIBUTED, USE_GLOO_IBVERBS
15 from .setup_helpers.nccl import USE_SYSTEM_NCCL, NCCL_INCLUDE_DIR, NCCL_ROOT_DIR, NCCL_SYSTEM_LIB, USE_NCCL
16 from .setup_helpers.rocm import ROCM_HOME, ROCM_VERSION, USE_ROCM
17 from .setup_helpers.nnpack import USE_NNPACK
18 from .setup_helpers.qnnpack import USE_QNNPACK
19 from .setup_helpers.cudnn import CUDNN_INCLUDE_DIR, CUDNN_LIB_DIR, CUDNN_LIBRARY, USE_CUDNN
20 
21 
22 from pprint import pprint
23 from glob import glob
24 import multiprocessing
25 import shutil
26 
27 
28 def which(thefile):
29  path = os.environ.get("PATH", os.defpath).split(os.pathsep)
30  for dir in path:
31  fname = os.path.join(dir, thefile)
32  fnames = [fname]
33  if IS_WINDOWS:
34  exts = os.environ.get('PATHEXT', '').split(os.pathsep)
35  fnames += [fname + ext for ext in exts]
36  for name in fnames:
37  if (os.path.exists(name) and os.access(name, os.F_OK | os.X_OK)
38  and not os.path.isdir(name)):
39  return name
40  return None
41 
42 
43 def cmake_version(cmd):
44  for line in check_output([cmd, '--version']).decode('utf-8').split('\n'):
45  if 'version' in line:
46  return LooseVersion(line.strip().split(' ')[2])
47  raise Exception('no version found')
48 
49 
50 def get_cmake_command():
51  cmake_command = 'cmake'
52  if IS_WINDOWS:
53  return cmake_command
54  cmake3 = which('cmake3')
55  if cmake3 is not None:
56  cmake = which('cmake')
57  if cmake is not None:
58  bare_version = cmake_version(cmake)
59  if bare_version < LooseVersion("3.5.0") and cmake_version(cmake3) > bare_version:
60  cmake_command = 'cmake3'
61  return cmake_command
62 
63 
64 def cmake_defines(lst, **kwargs):
65  for key in sorted(kwargs.keys()):
66  value = kwargs[key]
67  if value is not None:
68  lst.append('-D{}={}'.format(key, value))
69 
70 
71 # Ninja
72 # Use ninja if it is on the PATH. Previous version of PyTorch required the
73 # ninja python package, but we no longer use it, so we do not have to import it
74 USE_NINJA = not check_negative_env_flag('USE_NINJA') and (which('ninja') is not None)
75 
76 base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
77 install_dir = base_dir + "/torch"
78 build_type = "Release"
79 if DEBUG:
80  build_type = "Debug"
81 elif REL_WITH_DEB_INFO:
82  build_type = "RelWithDebInfo"
83 
84 
85 def overlay_windows_vcvars(env):
86  from distutils._msvccompiler import _get_vc_env
87  vc_arch = 'x64' if IS_64BIT else 'x86'
88  vc_env = _get_vc_env(vc_arch)
89  for k, v in env.items():
90  lk = k.lower()
91  if lk not in vc_env:
92  vc_env[lk] = v
93  return vc_env
94 
95 
96 def mkdir_p(dir):
97  try:
98  os.makedirs(dir)
99  except OSError:
100  pass
101 
102 
103 def create_build_env():
104  # XXX - our cmake file sometimes looks at the system environment
105  # and not cmake flags!
106  # you should NEVER add something to this list. It is bad practice to
107  # have cmake read the environment
108  my_env = os.environ.copy()
109  if USE_CUDNN:
110  my_env['CUDNN_LIBRARY'] = escape_path(CUDNN_LIBRARY)
111  my_env['CUDNN_INCLUDE_DIR'] = escape_path(CUDNN_INCLUDE_DIR)
112  if USE_CUDA:
113  my_env['CUDA_BIN_PATH'] = escape_path(CUDA_HOME)
114 
115  if IS_WINDOWS:
116  my_env = overlay_windows_vcvars(my_env)
117  return my_env
118 
119 
120 def run_cmake(version,
121  cmake_python_library,
122  build_python,
123  build_test,
124  build_dir,
125  my_env):
126  cmake_args = [
127  get_cmake_command()
128  ]
129  if USE_NINJA:
130  cmake_args.append('-GNinja')
131  elif IS_WINDOWS:
132  if IS_64BIT:
133  cmake_args.append('-GVisual Studio 15 2017 Win64')
134  else:
135  cmake_args.append('-GVisual Studio 15 2017')
136  try:
137  import numpy as np
138  NUMPY_INCLUDE_DIR = np.get_include()
139  USE_NUMPY = True
140  except ImportError:
141  USE_NUMPY = False
142  NUMPY_INCLUDE_DIR = None
143 
144  cflags = os.getenv('CFLAGS', "") + " " + os.getenv('CPPFLAGS', "")
145  ldflags = os.getenv('LDFLAGS', "")
146  if IS_WINDOWS:
147  cflags += " /EHa"
148 
149  mkdir_p(install_dir)
150  mkdir_p(build_dir)
151 
152  cmake_defines(
153  cmake_args,
154  PYTHON_EXECUTABLE=escape_path(sys.executable),
155  PYTHON_LIBRARY=escape_path(cmake_python_library),
156  PYTHON_INCLUDE_DIR=escape_path(distutils.sysconfig.get_python_inc()),
157  BUILDING_WITH_TORCH_LIBS="ON",
158  TORCH_BUILD_VERSION=version,
159  CMAKE_BUILD_TYPE=build_type,
160  BUILD_TORCH="ON",
161  BUILD_PYTHON=build_python,
162  BUILD_SHARED_LIBS=os.getenv("BUILD_SHARED_LIBS", "ON"),
163  BUILD_BINARY=check_env_flag('BUILD_BINARY'),
164  BUILD_TEST=build_test,
165  INSTALL_TEST=build_test,
166  BUILD_CAFFE2_OPS=not check_negative_env_flag('BUILD_CAFFE2_OPS'),
167  ONNX_NAMESPACE=os.getenv("ONNX_NAMESPACE", "onnx_torch"),
168  ONNX_ML=os.getenv("ONNX_ML", False),
169  USE_CUDA=USE_CUDA,
170  USE_DISTRIBUTED=USE_DISTRIBUTED,
171  USE_FBGEMM=not (check_env_flag('NO_FBGEMM') or check_negative_env_flag('USE_FBGEMM')),
172  USE_NUMPY=USE_NUMPY,
173  NUMPY_INCLUDE_DIR=escape_path(NUMPY_INCLUDE_DIR),
174  USE_SYSTEM_NCCL=USE_SYSTEM_NCCL,
175  NCCL_INCLUDE_DIR=NCCL_INCLUDE_DIR,
176  NCCL_ROOT_DIR=NCCL_ROOT_DIR,
177  NCCL_SYSTEM_LIB=NCCL_SYSTEM_LIB,
178  CAFFE2_STATIC_LINK_CUDA=check_env_flag('USE_CUDA_STATIC_LINK'),
179  USE_ROCM=USE_ROCM,
180  USE_NNPACK=USE_NNPACK,
181  USE_LEVELDB=check_env_flag('USE_LEVELDB'),
182  USE_LMDB=check_env_flag('USE_LMDB'),
183  USE_OPENCV=check_env_flag('USE_OPENCV'),
184  USE_QNNPACK=USE_QNNPACK,
185  USE_TENSORRT=check_env_flag('USE_TENSORRT'),
186  USE_FFMPEG=check_env_flag('USE_FFMPEG'),
187  USE_SYSTEM_EIGEN_INSTALL="OFF",
188  USE_MKLDNN=USE_MKLDNN,
189  USE_NCCL=USE_NCCL,
190  NCCL_EXTERNAL=USE_NCCL,
191  CMAKE_INSTALL_PREFIX=install_dir,
192  CMAKE_C_FLAGS=cflags,
193  CMAKE_CXX_FLAGS=cflags,
194  CMAKE_EXE_LINKER_FLAGS=ldflags,
195  CMAKE_SHARED_LINKER_FLAGS=ldflags,
196  THD_SO_VERSION="1",
197  CMAKE_PREFIX_PATH=os.getenv('CMAKE_PREFIX_PATH') or distutils.sysconfig.get_python_lib(),
198  BLAS=os.getenv('BLAS'),
199  CUDA_NVCC_EXECUTABLE=escape_path(os.getenv('CUDA_NVCC_EXECUTABLE')),
200  USE_REDIS=os.getenv('USE_REDIS'),
201  USE_GLOG=os.getenv('USE_GLOG'),
202  USE_GFLAGS=os.getenv('USE_GFLAGS'),
203  WERROR=os.getenv('WERROR'))
204 
205  if USE_GLOO_IBVERBS:
206  cmake_defines(cmake_args, USE_IBVERBS="1", USE_GLOO_IBVERBS="1")
207 
208  if USE_MKLDNN:
209  cmake_defines(cmake_args, MKLDNN_ENABLE_CONCURRENT_EXEC="ON")
210 
211  expected_wrapper = '/usr/local/opt/ccache/libexec'
212  if IS_DARWIN and os.path.exists(expected_wrapper):
213  cmake_defines(cmake_args,
214  CMAKE_C_COMPILER="{}/gcc".format(expected_wrapper),
215  CMAKE_CXX_COMPILER="{}/g++".format(expected_wrapper))
216  pprint(cmake_args)
217  for env_var_name in my_env:
218  if env_var_name.startswith('gh'):
219  # github env vars use utf-8, on windows, non-ascii code may
220  # cause problem, so encode first
221  try:
222  my_env[env_var_name] = str(my_env[env_var_name].encode("utf-8"))
223  except UnicodeDecodeError as e:
224  shex = ':'.join('{:02x}'.format(ord(c)) for c in my_env[env_var_name])
225  sys.stderr.write('Invalid ENV[{}] = {}\n'.format(env_var_name, shex))
226  # According to the CMake manual, we should pass the arguments first,
227  # and put the directory as the last element. Otherwise, these flags
228  # may not be passed correctly.
229  # Reference:
230  # 1. https://cmake.org/cmake/help/latest/manual/cmake.1.html#synopsis
231  # 2. https://stackoverflow.com/a/27169347
232  cmake_args.append(base_dir)
233  check_call(cmake_args, cwd=build_dir, env=my_env)
234 
235 
236 def build_caffe2(version,
237  cmake_python_library,
238  build_python,
239  rerun_cmake,
240  build_dir):
241  my_env = create_build_env()
242  build_test = not check_negative_env_flag('BUILD_TEST')
243  max_jobs = os.getenv('MAX_JOBS', None)
244  cmake_cache_file = 'build/CMakeCache.txt'
245  if rerun_cmake and os.path.isfile(cmake_cache_file):
246  os.remove(cmake_cache_file)
247  if not os.path.exists(cmake_cache_file) or (USE_NINJA and not os.path.exists('build/build.ninja')):
248  run_cmake(version,
249  cmake_python_library,
250  build_python,
251  build_test,
252  build_dir,
253  my_env)
254  if IS_WINDOWS:
255  if USE_NINJA:
256  # sccache will fail if all cores are used for compiling
257  j = max(1, multiprocessing.cpu_count() - 1)
258  if max_jobs is not None:
259  j = min(int(max_jobs), j)
260  check_call(['cmake', '--build', '.', '--target', 'install', '--config', build_type, '--', '-j', str(j)],
261  cwd=build_dir, env=my_env)
262  else:
263  check_call(['msbuild', 'INSTALL.vcxproj', '/p:Configuration={}'.format(build_type)],
264  cwd=build_dir, env=my_env)
265  else:
266  if USE_NINJA:
267  ninja_cmd = ['ninja', 'install']
268  if max_jobs is not None:
269  ninja_cmd += ['-j', max_jobs]
270  check_call(ninja_cmd, cwd=build_dir, env=my_env)
271  else:
272  max_jobs = max_jobs or str(multiprocessing.cpu_count())
273  check_call(['make', '-j', str(max_jobs), 'install'], cwd=build_dir, env=my_env)
274 
275  # in cmake, .cu compilation involves generating certain intermediates
276  # such as .cu.o and .cu.depend, and these intermediates finally get compiled
277  # into the final .so.
278  # Ninja updates build.ninja's timestamp after all dependent files have been built,
279  # and re-kicks cmake on incremental builds if any of the dependent files
280  # have a timestamp newer than build.ninja's timestamp.
281  # There is a cmake bug with the Ninja backend, where the .cu.depend files
282  # are still compiling by the time the build.ninja timestamp is updated,
283  # so the .cu.depend file's newer timestamp is screwing with ninja's incremental
284  # build detector.
285  # This line works around that bug by manually updating the build.ninja timestamp
286  # after the entire build is finished.
287  if os.path.exists('build/build.ninja'):
288  os.utime('build/build.ninja', None)
289 
290  if build_python:
291  for proto_file in glob('build/caffe2/proto/*.py'):
292  if os.path.sep != '/':
293  proto_file = proto_file.replace(os.path.sep, '/')
294  if proto_file != 'build/caffe2/proto/__init__.py':
295  shutil.copyfile(proto_file, "caffe2/proto/" + os.path.basename(proto_file))
296 
297 
298 def escape_path(path):
299  if os.path.sep != '/' and path is not None:
300  return path.replace(os.path.sep, '/')
301  return path
Module caffe2.python.layers.split.