ChatGPT解决这个技术问题 Extra ChatGPT

How to set linker flags for OpenMP in CMake's try_compile function

I would like to verify that the current compiler can build with openmp support. The application has do deploy across a wide variety of unix systems, some of which might have old versions of OpenMP, and I would like to test for important OpenMP functionality. So, I want to build a test source file that incorporates some of the OpenMP calls.

Thus, I created a very simple test file, and attempted to use the try_compile function from CMake. Ufortunately, it doesn't seem to apply the -fopenmp linker flag correctly. Does anyone know how to either force the linker flag or to see if the linker flag is being applied anywhere?

from CMakeLists.txt

try_compile(
    HAVE_OPENMP
    ${APBS_ROOT}/src/config
    ${APBS_ROOT}/src/config/omp_test.c
    CMAKE_FLAGS "-DCMAKE_C_FLAGS=-fopenmp -DCMAKE_EXE_LINKER_FLAGS=-fopenmp"
    OUTPUT_VARIABLE TRY_COMPILE_OUTPUT
    )

from omp_test.c

#include <stdio.h>
#include <omp.h>

int main()
{
    int i;
    int threadID = 0;
    #pragma omp parallel for private(i, threadID)
    for(i = 0; i < 16; i++ )
    {
        threadID = omp_get_thread_num();
        #pragma omp critical
        {
            printf("Thread %d reporting\n", threadID);
        }
    }
    return 0;
}

The resulting output is

Change Dir: src/config/CMakeFiles/CMakeTmp

Run Build Command:/usr/bin/make "cmTryCompileExec/fast"
/usr/bin/make -f CMakeFiles/cmTryCompileExec.dir/build.make CMakeFiles/cmTryCompileExec.dir/build
make[1]: Entering directory `src/config/CMakeFiles/CMakeTmp'
/usr/bin/cmake -E cmake_progress_report /data/work/source/apbs/src/config/CMakeFiles/CMakeTmp/CMakeFiles 1
Building C object CMakeFiles/cmTryCompileExec.dir/omp_test.c.o
/usr/bin/gcc    -o CMakeFiles/cmTryCompileExec.dir/omp_test.c.o   -c /data/work/source/apbs/src/config/omp_test.c
Linking C executable cmTryCompileExec
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTryCompileExec.dir/link.txt --verbose=1
/usr/bin/gcc         CMakeFiles/cmTryCompileExec.dir/omp_test.c.o  -o cmTryCompileExec -rdynamic 
CMakeFiles/cmTryCompileExec.dir/omp_test.c.o: In function `main':
omp_test.c:(.text+0x19): undefined reference to `omp_get_thread_num'
collect2: ld returned 1 exit status
make[1]: *** [cmTryCompileExec] Error 1
make[1]: Leaving directory `src/config/CMakeFiles/CMakeTmp'
make: *** [cmTryCompileExec/fast] Error 2

CMake Error at CMakeLists.txt:688 (message):
  Test OpenMP program would not build.  OpenMP disabled

When I try to compile the test program on the command line, it works fine

src/config$ gcc -fopenmp omp_test.c -o omp_test && ./omp_test
Thread 1 reporting
Thread 4 reporting
Thread 7 reporting
Thread 11 reporting
Thread 9 reporting
Thread 12 reporting
Thread 6 reporting
Thread 8 reporting
Thread 15 reporting
Thread 13 reporting
Thread 10 reporting
Thread 0 reporting
Thread 3 reporting
Thread 2 reporting
Thread 5 reporting
Thread 14 reporting

L
Levi Morrison

CMake has a standard module for testing if the compiler supports OpenMP:

find_package(OpenMP)
if (OPENMP_FOUND)
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
    set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()

Note:
This answer is not recommended to be used anymore for including OpenMP in the project for current CMake versions. Refer to the other answers.


Awesome! I wonder why I didn't find this in my searches... I assume it handles different compiler (icc, gcc, borland, etc) flags correctly?
Looking at the FindOpenMP.cmake source, it seems to handle GNU, MSVC, Intel, Sun, HP, IBM and MIPSpro compilers.
The linker flags need to be set, too. I don't know how to set them, but readers be warned.
If your OpenMP was pulled via your gcc install, would the find_package() call still be for OpenMP, or go by another name? I can't get CLion to perform as in the command line for a simple multi-threaded program using OpenMP!
CMAKE_EXE_LINKER_FLAGS didn't exist and was not needed. The module documentation doesn't say anything about that flag either. cmake.org/cmake/help/v3.0/module/FindOpenMP.html
L
Levi Morrison

As of CMake 3.9 there are imported OpenMP targets per language. I consider this to be a much more elegant solution. Here's an example in C++:

cmake_minimum_required(VERSION 3.9)
project(solver LANGUAGES CXX)

find_package(OpenMP REQUIRED)
add_executable(solver solver.cc)
target_link_libraries(solver PRIVATE OpenMP::OpenMP_CXX)

This is more convenient since it is less typing, and this way you don't have to adjust with compile flags, libraries, etc which are error-prone. This is the direction that modern CMake is going.

If you are working with something older than CMake 3.9 I still don't recommend the currently accepted answer. I believe setting the flags per-target is better:

add_executable(solver solver.cc)
target_link_libraries(solver PRIVATE "${OpenMP_CXX_FLAGS}")
target_compile_options(solver PRIVATE "${OpenMP_CXX_FLAGS}")

This may not work with some compilers; this is partly why CMake revamped its OpenMP support in CMake 3.9.


If you are using CLion: I thought that it was not working, because CMake was not able to find OpenMP. Then I tried on the command line and it worked. So it was a problem with CMake caches. Going to Tools -> CMake -> Reset Cache and Reload Project fixed it for me
Why PRIVATE and not PUBLIC as recommended by cliutils.gitlab.io/modern-cmake/chapters/packages/OpenMP.html? If my halfbaked understanding of CMake is right, PUBLIC is also plausible because linking a library built with OpenMP requires the -fopenmp flag again, see nanxiao.me/en/the-caveat-of-building-openmp-program. In practice it doesn't make a difference since it's on the default header/lib search paths anyway, but I would like to know the correct way.
"Why PRIVATE and not PUBLIC[?]" You only use PUBLIC if both the internal and external interfaces need it. I showed an executable, so in practice it's moot and I prefer to default to PRIVATE over PUBLIC. In a library it's still probably PRIVATE though, because you generally don't expose OpenMP types in your headers.
a
asendjasni

OpenMP support has been improved in CMake 3.9+

CMakeLists.txt

cmake_minimum_required(VERSION 3.9)
project(openmp_test) # you can change the project name

find_package(OpenMP)

add_executable(openmp_para_test main.cpp) # you can change the excutable name

if(OpenMP_CXX_FOUND)
    target_link_libraries(openmp_para_test PUBLIC OpenMP::OpenMP_CXX)
endif()

This way will correctly set the library link line differently from the compile line if needed.

Source.


Upvoted since it shows the correct way to include OpenMP (which is to use a target) in case it's an optional dependency, unlike existing answers including my own.
L
Levi Morrison

In case you try to use the "modern" way with g++, you could also do:

find_package(OpenMP REQUIRED)

add_executable(Foo foo.cpp)
target_compile_options(Foo PRIVATE -Wall ${OpenMP_CXX_FLAGS})
target_link_libraries(Foo PRIVATE ${OpenMP_CXX_FLAGS})

Notice:

If you would leave out only the target_compile_options your pragmas would simple be ignored (enabled warnings would tell you) If you would leave out only the target_link_libraries your code wouldn't compile, as g++ is not properly linked If you leave both out the 1. note applies rendering the linking no longer needed and your code would compile.

I don't know whether this linking "hack" with the flags is working for other compilers as well.


The variables should be OpenMP_CXX_FLAGS (no underline between Open and MP).

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now