OpenMP on High Sierra

Building OpenMP code is actually possible using Apple Clang that comes default with macOS’s Xcode! Although Apple does not build the OpenMP library, the compiler still supports it. In this post, I demonstrate the procedure necessary to include OpenMP in your build, both with the new support in CMake 3.12, as well as how it would be done without it.

Building OpenMP

The first order of business is to obtain a copy of the OpenMP runtime library. The procedure is outlined by the LLVM OpenMP project, but I would recommend using Homebrew. If you are using that, just run the following line:

brew install libomp

This will install the necessary files into $(brew --prefix libomp) (usually /usr/local/opt/libomp) and symbolically link them into $(brew --prefix) (usually /usr/local). You can always unlink from the main prefix directory by running brew unlink libomp.

Using OpenMP directly

If you want to build with OpenMP, you would normally do something like this:

clang++ -fopenmp myfile.cxx

But, as you probably know, this fails on Apple Clang due to the missing the built-in OpenMP library. But, Apple Clang does allow you to process the OpenMP pragmas with -Xpreprocessor -fopenmp, and then you can manually link to the OpenMP library. Assuming /usr/local contains OpenMP and you have your paths set up for it, you can simply run:

clang -Xpreprocessor -fopenmp -lomp myfile.cxx

If you don’t, you need to add explicit directories:

clang -Xpreprocessor -fopenmp -lomp -I"$(brew --prefix libomp)/include" -L"$(brew --prefix libomp)/lib" myfile.cxx

In this case, you might need to also set DYLD_LIBRARY_PATH for the runtime library to be correctly discovered.

Using OpenMP in CMake

Modern method:

CMake 3.12 has built in support for AppleClang’s OpenMP, as long as you use the target based system. An example of a CMakeLists.txt that would support OpenMP on macOS:

cmake_minimum_required(VERSION 3.12)
project(openmptest CXX)

add_executable(sample sample.cpp)

find_package(OpenMP REQUIRED)
target_link_libraries(sample PRIVATE OpenMP::OpenMP_CXX)

Thanks to Roman Bange for the example. You can also conditionally add the OpenMP target if it doesn’t exist, if you want to support a wide range of CMakes (I recommend simply requiring CMake 3.12 if the user wants OpenMP + Apple). See Modern CMake’s OpenMP example or the example at the end of this post.

Classic method:

If you want to use OpenMP in CMake older than 3.12, or if the library you are building does not use the target system, you can either add the following to the command line when you build:

-DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp -I$(brew --prefix libomp)/include" -DOpenMP_CXX_LIB_NAMES="omp" -DOpenMP_omp_LIBRARY=$(brew --prefix libomp)/lib/libomp.a

You’ll want a recent version of CMake, and you’ll need the package author to use the OpenMP::OpenMP_CXX target instead of linking to the flags (which used to be common in older CMakes but is incorrect).

Or, if you are working on a CMakeLists, you can include my cmake helpers in your module path and include(PatchAppleOpenmp), and those lines will be added if you are using Apple Clang and have the Homebrew package.

Bonus: Using FindOpenMP correctly:

This is the best way to add OpenMP to be compatible with CMake 3.1+. I’m assuming OpenMP is required and that you are working on a target called MyProgram:

find_package(OpenMP REQUIRED)
if(NOT TARGET OpenMP::OpenMP_CXX)
    add_library(OpenMP_TARGET INTERFACE)
    add_library(OpenMP::OpenMP_CXX ALIAS OpenMP_TARGET)
    target_compile_options(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS})
    find_package(Threads REQUIRED)
    target_link_libraries(OpenMP_TARGET INTERFACE Threads::Threads)
    target_link_libraries(OpenMP_TARGET INTERFACE ${OpenMP_CXX_FLAGS})
endif()

target_link_libraries(MyLibraries PUBLIC OpenMP::OpenMP_CXX)
comments powered by Disqus