如何在 CMake 的 try_compile 函数中为 OpenMP 设置链接器标志

Posted

技术标签:

【中文标题】如何在 CMake 的 try_compile 函数中为 OpenMP 设置链接器标志【英文标题】:How to set linker flags for OpenMP in CMake's try_compile function 【发布时间】:2012-09-06 03:17:40 【问题描述】:

我想验证当前编译器是否可以使用 openmp 支持进行构建。该应用程序确实部署在各种 unix 系统中,其中一些系统可能具有旧版本的 OpenMP,我想测试重要的 OpenMP 功能。所以,我想构建一个包含一些 OpenMP 调用的测试源文件。

因此,我创建了一个非常简单的测试文件,并尝试使用 CMake 中的 try_compile 函数。不幸的是,它似乎没有正确应用 -fopenmp 链接器标志。有谁知道如何强制链接器标志或查看链接器标志是否在任何地方应用?

来自 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
    )

来自 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;

结果输出是

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

当我尝试在命令行上编译测试程序时,它工作正常

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

【问题讨论】:

【参考方案1】:

OpenMP 支持在 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()

如果需要,这种方式将正确设置库链接行与编译行不同。

Source.

【讨论】:

赞成,因为它显示了包含 OpenMP(即使用目标)的正确方法,以防它是可选依赖项,这与包括我自己在内的现有答案不同。【参考方案2】:

CMake 有一个standard module 用于测试编译器是否支持 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()

注意: 对于当前的CMake 版本,不建议再使用此答案将OpenMP 包含在项目中。参考其他答案。

【讨论】:

太棒了!我想知道为什么我在搜索中没有找到它...我认为它可以正确处理不同的编译器(icc、gcc、borland 等)标志? 查看 FindOpenMP.cmake 源码,它似乎可以处理 GNU、MSVC、Intel、Sun、HP、IBM 和 MIPSpro 编译器。 链接器标志也需要设置。我不知道如何设置它们,但请读者注意。 如果您的 OpenMP 是通过 gcc 安装提取的,那么 find_package() 调用是否仍然适用于 OpenMP,还是使用其他名称?对于使用 OpenMP 的简单多线程程序,我无法让 CLion 在命令行中执行! CMAKE_EXE_LINKER_FLAGS 不存在且不需要。模块文档也没有说明该标志。 cmake.org/cmake/help/v3.0/module/FindOpenMP.html【参考方案3】:

从 CMake 3.9 开始,每种语言都有导入的 OpenMP 目标。我认为这是一个更优雅的解决方案。下面是一个 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)

这更方便,因为它减少了输入,这样您就不必调整容易出错的编译标志、库等。这就是现代 CMake 的发展方向。


如果您使用的是 CMake 3.9 之前的版本,我仍然不推荐使用 currently accepted answer。我相信为每个目标设置标志更好:

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

这可能不适用于某些编译器;这也是 CMake 在 CMake 3.9 中改进其 OpenMP 支持的部分原因。

【讨论】:

如果您使用的是 CLion:我认为它不起作用,因为 CMake 无法找到 OpenMP。然后我在命令行上尝试了它,它起作用了。所以这是 CMake 缓存的问题。转到工具 -> CMake -> 重置缓存并重新加载项目为我修复了它 为什么要按照cliutils.gitlab.io/modern-cmake/chapters/packages/OpenMP.html 的建议进行私有而不是公开?如果我对 CMake 的半生不熟的理解是正确的,那么 PUBLIC 也是合理的,因为链接使用 OpenMP 构建的库需要再次使用 -fopenmp 标志,请参阅nanxiao.me/en/the-caveat-of-building-openmp-program。实际上它并没有什么区别,因为它无论如何都在默认的 header/lib 搜索路径上,但我想知道正确的方法。 "为什么是 PRIVATE 而不是 PUBLIC[?]" 只有在内部和外部接口都需要它时才使用 PUBLIC。我展示了一个可执行文件,所以在实践中它没有实际意义,我更喜欢默认为 PRIVATE 而不是 PUBLIC。但在库中,它可能仍然是 PRIVATE,因为您通常不会在标头中公开 OpenMP 类型。【参考方案4】:

如果您尝试使用 g++ 的“现代”方式,您也可以这样做:

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)

注意:

    如果您只省略 target_compile_options,您的编译指示将被简单地忽略(启用的警告会告诉您)

    如果您只省略 target_link_libraries,您的代码将无法编译,因为 g++ 未正确链接

    如果您同时忽略 1. 注释,则应用呈现不再需要的链接并且您的代码将编译。

我不知道这种将“hack”与标志链接起来是否也适用于其他编译器。

【讨论】:

变量应该是OpenMP_CXX_FLAGSOpenMP之间没有下划线)。

以上是关于如何在 CMake 的 try_compile 函数中为 OpenMP 设置链接器标志的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CMake GUI 中显示 FooConfig.cmake 文件中的 CMake 变量?

如何添加在cmake的宏的定义是啥

如何在Windows开发环境中用CMake改变CMAKE_INSTALL_PREFIX的值?

make如何指定cmake路径

如何在centos7中安装cmake

如何在我的 cmake 项目中包含另一个 cmake 项目的头文件?