CMake:target_link_libraries包含为SYSTEM以抑制编译器警告

lokaqttq  于 2022-11-11  发布在  其他
关注(0)|答案(5)|浏览(366)

为了抑制来自我在应用程序中使用的库的编译器警告,我在使用target_link_libraries添加它们之前,手动将它们的目录作为系统库包含在target_include_directories(myapp SYSTEM ...)中,如下所示:

add_executable(myapp myapp.cpp)
target_include_directories(myapp SYSTEM
  PRIVATE "extern/lib/include"
)
target_link_libraries(myapp lib::lib)

但是,如果lib的开发者决定改变include路径,这会让人觉得很麻烦,也会被破坏。如果只使用target_link_library,这不会是一个问题,但是,当然,它们是通过-I包含的,我会再次收到来自这个include的编译器警告。
有没有更优雅、更安全的方法来实现这一点?如果target_link_libraries有一个SYSTEM选项来告诉cmake将其作为系统库包含进来,那就太好了。

2izufjch

2izufjch1#

我定义了一个函数来处理这个问题:

function(target_link_libraries_system target)
  set(libs ${ARGN})
  foreach(lib ${libs})
    get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
    target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
    target_link_libraries(${target} ${lib})
  endforeach(lib)
endfunction(target_link_libraries_system)

我现在可以调用target_link_libraries_system(myapp lib::lib),并从目标的属性中读取包含目录。
这可以扩展为选择性地指定PUBLIC|PRIVATE|INTERFACE范围:

function(target_link_libraries_system target)
  set(options PRIVATE PUBLIC INTERFACE)
  cmake_parse_arguments(TLLS "${options}" "" "" ${ARGN})
  foreach(op ${options})
    if(TLLS_${op})
      set(scope ${op})
    endif()
  endforeach(op)
  set(libs ${TLLS_UNPARSED_ARGUMENTS})

  foreach(lib ${libs})
    get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
    if(lib_include_dirs)
      if(scope)
        target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
      else()
        target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs})
      endif()
    else()
      message("Warning: ${lib} doesn't set INTERFACE_INCLUDE_DIRECTORIES. No include_directories set.")
    endif()
    if(scope)
      target_link_libraries(${target} ${scope} ${lib})
    else()
      target_link_libraries(${target} ${lib})
    endif()
  endforeach()
endfunction(target_link_libraries_system)

如果库没有设置其INTERFACE_INCLUDE_DIRECTORIES属性,这个扩展版本也会输出一个警告。

js5cn81o

js5cn81o2#

我修改了塞巴斯蒂安的解决方案以包括范围。

function(target_link_libraries_system target scope)
  set(libs ${ARGN})
  foreach(lib ${libs})
    get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES)
    target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs})
    target_link_libraries(${target} ${scope} ${lib})
  endforeach(lib)
endfunction(target_link_libraries_system)
svmlkihl

svmlkihl3#

这是在CMake discourse和@ben.boeckel(CMake开发人员)中提出的问题,回答说:
IMPORTED目标应该已经将其包含目录视为SYSTEM。可以使用NO_SYSTEM_FROM_IMPORTED目标属性来禁用它。

ovfsdjhp

ovfsdjhp4#

可能的陷阱:如果你和我一样,通过启用toplevel目录中的所有警告来启动每个项目...


# Warning level

add_compile_options(
    -Wall -Wextra -Wpedantic

    -Werror=switch
    -Werror=return-type
    -Werror=uninitialized
    -Werror=format-security
    -Werror=reorder
    -Werror=delete-non-virtual-dtor
    $<$<CONFIG:Debug>:-Werror>
)

...那么,这显然会被所有子项目继承(比如git子模块,或者其他)。
如果是 * 这种 * 情况,解决方案很简单-请具体说明:在子目录中执行此操作,并且/或者使用target_compile_options,以获得更好的效果:

target_compile_options(myTarget PRIVATE
    ...
)
nwlqm0z1

nwlqm0z15#

首先,您还没有指定要链接的目标是否为IMPORTED。我假设它是IMPORTED,因为IMPORTED目标的包含目录默认为SYSTEM。注意:可以禁用此行为。在CMake v3.25之前,可以使用NO_SYSTEM_FROM_IMPORTEDIMPORTED_NO_SYSTEM之一。对于CMake 3.25和更高版本,可以修改目标的SYSTEM属性。
这通常只剩下通过add_subdirectory添加的目标,其中包括FetchContent在非find_package模式下添加的目标。在这种情况下,您可以看到FetchContent Q&A hereadd_subdirectory Q&A here。总之,对于CMake v3.25之前的版本,请使用一种解决方法,将INTERFACE_INCLUDE_DIRECTORIES目标属性复制/移动到INTERFACE_SYSTEM_INCLUDE_DIRECTORIES目标属性。对于CMake v3.25或更高版本,您可以修改SYSTEM目标属性或SYSTEM目录属性,或者使用add_subdirectory/FetchContent_DeclareSYSTEM参数。

相关问题