英文:
How to include target include directories transitive through multiple linked libraries
问题
我们正在进行一个嵌入式项目,使用C/C++编写,目前出现了一些特殊需求。背景是有两个编译的库定义了相同的符号。编译器允许创建可重定位输出模块(通过部分链接),并在链接时隐藏其他编译单元的符号。这还意味着输出模块不需要定义所有的符号,这将在最终链接中完成。所使用的编译器是TI LTS1.3.0。我将直接链接到手册的可重定位部分:链接到手册
项目的另一部分几乎完全基于CMake构建,使用静态库通过target_link_libraries
相互链接。
为了使这个工作正常,我为这两个输出模块中的每个创建了一个"add_executable"目标,这些目标具有相同的符号。我通过CMake传递了静态库,然后使用target_link_libraries
将它们链接到输出模块。但现在出现了问题。所有静态库的内容都被编译到这两个输出模块中。这是不希望的行为,因为正如我所说,最终的链接工作应该由另一个CMake的"add_executable"命令来完成,将缺失的部分链接到它。
使用目标的包含目录属性不太合适,因为它只会添加给定目标本身的包含目录,而不会添加它将包含和链接的目标的包含目录。
因此,如果你有(伪代码):
#库A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #包含A.h
endfunction()
#库B。不同的位置
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #包含B.h
endfunction()
#输出模块,进行部分链接。只应链接和编译LIBTOBELINKEDIN,而不是libB。不同的位置。
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c 包含A.h
#以下会导致libA和libB也被编译和链接到输出中,因为根据我了解的传递关系,这是不希望的。
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#尝试这个
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#只会包含B.h,但不包含A.h。编译将失败。
target_include_directories(outputModuleA /inc ${libBInc})
我在CMake本身没有找到解决这个问题的方法。这让我感到困惑,因为在库传递时,文档中已经指出,必须知道所有的包含目录。但我明白,仅仅获取传递的库的目标包含目录不包括其他目标的目录。
因为target_link_libraries
也不能以这种方式工作,我只能考虑可能的递归解决方案?但对于这个问题,我的知识几乎是不存在的。
使用类似HEADERS_ONLY
的target_link_libraries
选项可能对此工作有所帮助。
另外,可以说:如果输出模块包含了所有的定义,那就不会有问题,因为链接器会知道它们并完成其工作。但这也是不希望的,因为我们使用生成的静态库将它们直接放入RAM的不同区域的部分。这将意味着创建另一个用于部分链接的链接器脚本,定义可以再次移动的部分。但我们走得越远,就越不需要CMake。
英文:
we are working on an embedded project in C/C++ and currently some special needs appeared. Background is there are two compiled libraries which define the same symbols. The compiler allows to create relocatable output modules (with partial linking) and to hide symbols for other compilation units when linking. This also means the output module does not need to have all the symbols defined, this will be done in the final linking. Compiler used is TI LTS1.3.0. I will link directly to the relocatable-section of the manual: https://software-dl.ti.com/codegen/docs/tiarmclang/rel1_3_0_LTS/compiler_manual/linker_description/04_linker_options/linker-output-options.html#stdz0756429
The other part of the project is hardly built on CMake with static libraries which are linked against each other via target_link_libraries.
To get this working I created an "add_executable"-target for each of those both output modules with the same symbols. To those I pass the static-libraries by CMake and get the linked with target_link_libraries.
But now I have a problem. All contents of the static libraries are compiled in each of those output modules. This is unwanted behaviour since as said the final linking does the job of linking the missing stuff - so the static-libraries - to it. This should be done with another add_executable command via CMake as well.
using the target include directories property is not suitable since it only adds the include directories of the given target itself but not of the target the target will include and link against.
So e.g. if you have (pseudo code):
#library A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #contains A.h
endfunction()
#library B. different location
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #contains B.h
endfunction()
#target output module with partial linking. Only should link and compile LIBTOBELINKEDIN, not libB. different location.
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c does include A.h
#following would cause libA and libB also to be compiled and linked in the output due to transitive stuff as I understood, which is unwanted.
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#trying this
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#will only include B.h but not A.h. compilation will fail.
target_include_directories(outputModuleA /inc ${libBInc})
I did not find any solution in Cmake itself to solve this problem. It's confusing me since all the include-directories must be known when the libraries are passed transitive, which is stated in the documentation. But I understand that getting the target include directories of just the passed lib does not include the other ones.
Since target_link_libraries does also not work this way I can only think of a maybe recursive solution? But for that my knowledge is just non-existent.
target_link_libraries with something like HEADERS_ONLY would be helpfull for this job.
Also one can say: if the output module contains all the definitions it won't be a problem, since the linker then knows them and will do its magic.
But this is also unwanted, since we use the generated static-libraries to place them into sections in different regions of the RAM directly. This would then mean to create another linker-script for partial linking which defines sections which then can be again moved. But the more we go this direction, the less we need CMake for it.
答案1
得分: 1
使用$<TARGET_PROPERTY>
生成表达式,而不是get_target_property
:通过该表达式提取的属性值已经包括传递传播:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
请注意,生成表达式 的使用有限:并非所有函数都支持它们。target_include_directories 的文档明确说明该命令支持生成表达式。
英文:
Instead of get_target_property
use $<TARGET_PROPERTY>
generator expression: the property's value, extracted by that expression, already includes transitive propagation:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
Note, that generator expressions has limited usage: not all functions expects them. Documentation for target_include_directories clearly states that the command supports generator expressions.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论