Skip to content

Commit 0d5f0a8

Browse files
malfetpytorchmergebot
authored andcommitted
[CMake] Find HomeBrew OpenMP on MacOS (pytorch#145870)
Either via `OMP_PREFIX` envvar or by searching in `/opt/homebrew/opt/libomp` folder Modify libomp bundling logic in setup.py to change absolute path to libomp.dylib to a relative one if necessary Pull Request resolved: pytorch#145870 Approved by: https://github.com/Skylion007, https://github.com/atalman ghstack dependencies: pytorch#145871
1 parent 116af80 commit 0d5f0a8

File tree

2 files changed

+52
-17
lines changed

2 files changed

+52
-17
lines changed

cmake/Modules/FindOpenMP.cmake

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ cmake_policy(SET CMP0012 NEW) # if() recognizes numbers and booleans
7979
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
8080
cmake_policy(SET CMP0057 NEW) # if IN_LIST
8181

82+
83+
if(NOT "$ENV{OMP_PREFIX}" STREQUAL "")
84+
set(OpenMP_PREFIX "$ENV{OMP_PREFIX}")
85+
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" AND EXISTS /opt/homebrew/opt/libomp)
86+
set(OpenMP_PREFIX "/opt/homebrew/opt/libomp")
87+
endif()
88+
8289
function(_OPENMP_FLAG_CANDIDATES LANG)
8390
if(NOT OpenMP_${LANG}_FLAG)
8491
unset(OpenMP_FLAG_CANDIDATES)
@@ -93,7 +100,7 @@ function(_OPENMP_FLAG_CANDIDATES LANG)
93100
else()
94101
# AppleClang may need a header file, search for omp.h with hints to brew
95102
# default include dir
96-
find_path(__header_dir "omp.h" HINTS "/usr/local/include")
103+
find_path(__header_dir "omp.h" HINTS "/usr/local/include" "${OpenMP_PREFIX}/include")
97104
endif()
98105
set(OMP_FLAG_AppleClang "-Xpreprocessor -fopenmp" "-Xpreprocessor -fopenmp -I${__header_dir}")
99106

@@ -273,6 +280,16 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
273280
mark_as_advanced(OpenMP_libomp_LIBRARY)
274281
endif()
275282

283+
# Use OpenMP_PREFIX if defined
284+
if (NOT OpenMP_libomp_LIBRARY AND NOT "${OpenMP_PREFIX}" STREQUAL "")
285+
find_library(OpenMP_libomp_LIBRARY
286+
NAMES omp gomp iomp5
287+
HINTS "${OpenMP_PREFIX}/lib"
288+
DOC "libomp location for OpenMP"
289+
)
290+
mark_as_advanced(OpenMP_libomp_LIBRARY)
291+
endif()
292+
276293
if(OpenMP_libomp_LIBRARY MATCHES "iomp5")
277294
set(OpenMP_libiomp5_LIBRARY "${MKL_OPENMP_LIBRARY}" CACHE STRING "libiomp5 location for OpenMP")
278295
if("-fopenmp=libiomp5" IN_LIST OpenMP_${LANG}_FLAG_CANDIDATES)
@@ -666,5 +683,6 @@ unset(OpenMP_Fortran_TEST_SOURCE)
666683
unset(OpenMP_C_CXX_CHECK_VERSION_SOURCE)
667684
unset(OpenMP_Fortran_CHECK_VERSION_SOURCE)
668685
unset(OpenMP_Fortran_INCLUDE_LINE)
686+
unset(OpenMP_PREFIX)
669687

670688
cmake_policy(POP)

setup.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -572,35 +572,52 @@ def _embed_libomp(self):
572572
assert rpath.startswith("path ")
573573
rpaths.append(rpath.split(" ", 1)[1].rsplit("(", 1)[0][:-1])
574574

575-
omp_lib_name = (
576-
"libomp.dylib" if os.uname().machine == "arm64" else "libiomp5.dylib"
577-
)
578-
omp_rpath_lib_path = os.path.join("@rpath", omp_lib_name)
579-
if omp_rpath_lib_path not in libs:
575+
omplib_path = get_cmake_cache_vars()["OpenMP_libomp_LIBRARY"]
576+
omplib_name = get_cmake_cache_vars()["OpenMP_C_LIB_NAMES"] + ".dylib"
577+
omplib_rpath_path = os.path.join("@rpath", omplib_name)
578+
579+
# This logic is fragile and checks only two cases:
580+
# - libtorch_cpu depends on `@rpath/libomp.dylib`e (happens when built inside miniconda environment)
581+
# - libtorch_cpu depends on `/abs/path/to/libomp.dylib` (happens when built with libomp from homebrew)
582+
if not any(c in libs for c in [omplib_path, omplib_rpath_path]):
580583
return
581584

582585
# Copy libomp/libiomp5 from rpath locations
586+
target_lib = os.path.join(self.build_lib, "torch", "lib", omplib_name)
587+
libomp_relocated = False
583588
for rpath in rpaths:
584-
source_lib = os.path.join(rpath, omp_lib_name)
589+
source_lib = os.path.join(rpath, omplib_name)
585590
if not os.path.exists(source_lib):
586591
continue
587-
target_lib = os.path.join(self.build_lib, "torch", "lib", omp_lib_name)
588592
self.copy_file(source_lib, target_lib)
589593
# Delete old rpath and add @loader_lib to the rpath
590594
# This should prevent delocate from attempting to package another instance
591595
# of OpenMP library in torch wheel as well as loading two libomp.dylib into
592596
# the address space, as libraries are cached by their unresolved names
593-
subprocess.check_call(
594-
[
595-
"install_name_tool",
596-
"-rpath",
597-
rpath,
597+
install_name_tool_args = [
598+
"-rpath",
599+
rpath,
600+
"@loader_path",
601+
]
602+
libomp_relocated = True
603+
break
604+
if not libomp_relocated and os.path.exists(omplib_path):
605+
self.copy_file(omplib_path, target_lib)
606+
install_name_tool_args = [
607+
"-change",
608+
omplib_path,
609+
omplib_rpath_path,
610+
]
611+
if "@loader_path" not in rpaths:
612+
install_name_tool_args += [
613+
"-add_rpath",
598614
"@loader_path",
599-
libtorch_cpu_path,
600615
]
601-
)
602-
break
603-
616+
libomp_relocated = True
617+
if libomp_relocated:
618+
install_name_tool_args.insert(0, "install_name_tool")
619+
install_name_tool_args.append(libtorch_cpu_path)
620+
subprocess.check_call(install_name_tool_args)
604621
# Copy omp.h from OpenMP_C_FLAGS and copy it into include folder
605622
omp_cflags = get_cmake_cache_vars()["OpenMP_C_FLAGS"]
606623
if not omp_cflags:

0 commit comments

Comments
 (0)