cmake:target属性POSITION_INDEPENDENT_CODE和INTERFACE_POSITION_INDEPENDENT_CODE的区别

cmake定义的target有两个名字类似的属性:POSITION_INDEPENDENT_CODEINTERFACE_POSITION_INDEPENDENT_CODE,本文说明它们的含义和区别

-fPIC

介绍POSITION_INDEPENDENT_CODEINTERFACE_POSITION_INDEPENDENT_CODE属性前先介绍一下-fPIC编译选项。
-fPIC是gcc编译器的编译参数,以下是机器人告诉我的关于-fPIC参数的作用

在GCC编译器中, -fPIC 参数是指生成位置无关代码(Position Independent Code,PIC)。位置无关代码是一种可在内存中的任何位置加载和执行的代码。它通常用于动态链接库(shared library)的编译。

使用 -fPIC 参数编译代码时,生成的目标文件中的代码和数据引用都使用相对地址,而不是绝对地址。这样,当目标文件被加载到内存中时,它可以被放置在任何可用的内存地址上,而不会发生地址冲突。

通过使用位置无关代码,可以使得动态链接库在不同的内存地址空间中被加载和共享,提供更高的灵活性和可移植性。这对于操作系统和应用程序来说是非常重要的,因为它们可以在不同的环境中加载和使用这些动态链接库,而无需担心地址冲突和重新编译的问题。

总结来说, -fPIC 参数的作用是生成位置无关代码,用于编译动态链接库,以提供更高的灵活性和可移植性。

可以看出-fPIC参数是用于动态库的编译参数。

POSITION_INDEPENDENT_CODE

定义 -fPIC 参数最直接的方式是通过CMAKE_CXX_FLAGSCMAKE_C_FLAGS参数定义,
示例如下,因为它只是clang和gcc才有的参数所以在设置-fPIC参数的时候需要判断编译器

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
endif()

但这样在跨平台项目编译时需要更多的维护工作量,为了让CMakeLists.txt脚本更简洁,减少编译器无关性,通过POSITION_INDEPENDENT_CODE属性来定义-fPIC参数是推荐的方式:
POSITION_INDEPENDENT_CODE是cmake为target定义的属性
可以通过set_property,set_target_properties函数来定义POSITION_INDEPENDENT_CODE属性

## set_property示例
set_property(TARGET my_target PROPERTY POSITION_INDEPENDENT_CODE ON)
## set_target_properties 示例
set_target_properties (my_target PROPERTIES POSITION_INDEPENDENT_CODE ON)

根据cmake官方文档说明,当target为动态库时POSITION_INDEPENDENT_CODE 默认值为True,否则为静态库时默认为False;
参见 POSITION_INDEPENDENT_CODE

CMAKE_POSITION_INDEPENDENT_CODE

注意POSITION_INDEPENDENT_CODE是target的属性,所以set_property,set_target_properties 调用只对target有效,而通过CMAKE_CXX_FLAGSCMAKE_C_FLAGS参数定义定义 -fPIC 参数对所有target有效。如果也希望一次定义所有target的POSITION_INDEPENDENT_CODE属性,则可以通过设置CMAKE_POSITION_INDEPENDENT_CODE变量来实现,用于定义所有target的POSITION_INDEPENDENT_CODE属性的默认值

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

INTERFACE_POSITION_INDEPENDENT_CODE

INTERFACE_POSITION_INDEPENDENT_CODE也是target的属性,但它的作用与POSITION_INDEPENDENT_CODE不同
INTERFACE_POSITION_INDEPENDENT_CODE 属性通知消费者(即依赖于当前target的target)是否需要将他们的 POSITION_INDEPENDENT_CODE 属性设置为ON。如果该属性被设置为ON,那么所有消费者的 POSITION_INDEPENDENT_CODE 属性也将被设置为ON。同样地,如果该属性被设置为OFF,那么所有消费者的 POSITION_INDEPENDENT_CODE 属性也将被设置为OFF。如果该属性未定义,那么消费者将通过其他方式确定他们的 POSITION_INDEPENDENT_CODE 属性。
总结就是INTERFACE_POSITION_INDEPENDENT_CODE 用于确保消费者与链接的目标的POSITION_INDEPENDENT_CODE 属性保持一致性。

简单来说就是
如果一个target定义了INTERFACE_POSITION_INDEPENDENT_CODE属性并不会影响自己的POSITION_INDEPENDENT_CODE属性,而是会影响依赖它的Target的POSITION_INDEPENDENT_CODE属性

add_library(a STATIC a.cpp)
set_target_properties (a PROPERTIES 
	POSITION_INDEPENDENT_CODE ON 
	INTERFACE_POSITION_INDEPENDENT_CODE ON)
add_library(b SHARED b.cpp)

如上示例中,静态库a设置了POSITION_INDEPENDENT_CODEONINTERFACE_POSITION_INDEPENDENT_CODEON,因为INTERFACE_POSITION_INDEPENDENT_CODE 的传递作用,
动态库b的POSITION_INDEPENDENT_CODE属性自动为ON

INTERFACE_POSITION_INDEPENDENT_CODE 属性实际只有静态库需要设置,对于动态库不需要设置该属性,因为动态库不需要依赖库保存位置无关代码(PIC)一致性

位置无关代码一致性要求

前面说了-fPIC参数是用于动态库的编译参数。但对于静态库有时也需要指定-fPIC编译出位置无关代码,因为一个动态库连接静态库时,如果其连接的静态库都不是编译为位置无关代码代码(-fPIC),则在连接阶段可能会报错:

/usr/bin/ld: …/…/static.a(file.cpp.o): relocation R_X86_64_TPOFF32 against symbol `_ZGVZN6spdlog7details2os9thread_idEvE3tid’ can not be used when making a shared object; recompile with -fPIC

参考资料

https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html
https://cmake.org/cmake/help/latest/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE.html
https://cmake.org/cmake/help/latest/variable/CMAKE_POSITION_INDEPENDENT_CODE.html

你可能感兴趣的:(cmake,c++,cmake,-fPIC)