我开始使用 C 来使用 CMAKE,实际上我正在创建两个非常小的静态库。
我的目标是:
这些库被编译并链接到 *.a 文件中。 [这工作] 然后我希望将 *.a 文件复制到 /usr/local/lib [这也有效] 据我所知(很少),它们是使用 -lnameoflib 链接的,这是一个编译器标志.好的。我已经准备好我的 CMakeLists.txt,它实际上将 *.a 文件复制到 /usr/local/lib 中。但是,为了能够在程序中使用它们,我还需要将它们的头文件复制到 /usr/local/include 中,然后我可以通过简单的方法 #include
我的问题是 - 使用 CMAKE 将头文件复制到 /usr/include 文件夹的正确方法是什么?我希望它在执行 make install
时自动复制它们,就像 *.a 文件一样。
对于这两个库,我都有一个类似的 CMakeLists.txt:
project(programming-network)
add_library(programming-network STATIC
send_string.c
recv_line.c
)
INSTALL(TARGETS programming-network
DESTINATION "lib"
)
install: \n\tcp $INCLUDES/* /usr/include/
下的 Makefile
中添加一行?
-DCMAKE_INSTALL_PREFIX=/usr
调用 CMake 之后安装的,那么您的 lib 最终会在 /usr/lib
中(正如预期的前缀设置为 /usr
),但您的标头最终会在 /include
中(可能不是预期的) . Per的回答更有意义。
最新 cmake 版本的更好方法是使用目标的 PUBLIC_HEADER
属性。
project(myproject)
add_library(mylib some.c another.c)
set_target_properties(mylib PROPERTIES PUBLIC_HEADER "some.h;another.h")
INSTALL(TARGETS mylib
LIBRARY DESTINATION some/libpath
PUBLIC_HEADER DESTINATION some/includepath
)
一些参考:
以更好的方式,将复制与模式匹配的所有文件并保留目录结构。
INSTALL (
DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION include
FILES_MATCHING PATTERN "*.h*")
${CMAKE_CURRENT_SOURCE_DIR}/include/gtk-4.0/gsk/gsk.h
、${CMAKE_CURRENT_SOURCE_DIR}/include/gtk-4.0/gsk/vulkan/gskvulkanrenderer.h
。
我不认为你的解决方案是正确的。 /usr/include
应保留给您的供应商以放入文件。
IMO 的正确做法是在 /usr/local/include
中安装标头,然后指示用户安装 export CPATH="/usr/local/include:${CPATH}"
。
似乎 /usr/local/lib
是自动搜索的,但如果您希望使用另一个目录 export LIBRARY_PATH="/usr/local/lib:${LIBRARY_PATH}"
对 .a 二进制文件的工作方式类似(但取决于您的操作系统,对于共享库可能适用也可能不好)。
可选,但更麻烦的是在编译时添加 -I /usr/local/include
和 -L /usr/local/lib
。
这是一个有点主观的答案,但对我来说效果很好。
除了 the accepted answer,如果您要创建大量库并且 set_property
语法会让您望而却步。您可以将其包装在一个非常简单的宏中,例如:
# File: target_public_headers.cmake
macro(target_public_headers TARGET)
set_target_properties(${TARGET} PROPERTIES PUBLIC_HEADER "${ARGN}")
endmacro()
然后你可以像这样使用它:
project(myproject)
include(target_public_headers)
add_library(mylib some.c another.c)
target_public_headers(mylib some.h another.h) # <<<<<
# If you're exporting this library then you need to tell
# CMake how to include the "installed" version of the headers.
target_include_directories(mylib
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PUBLIC $<INSTALL_INTERFACE:some/includepath>
)
INSTALL(TARGETS mylib
LIBRARY DESTINATION some/libpath
PUBLIC_HEADER DESTINATION some/includepath
)
多年后,使用 CMake 3.23,我们可以将 FILE_SET 用于公共标头:
project(programming-network)
add_library(programming-network STATIC)
target_include_directories(programming-network PRIVATE "${PROJECT_SOURCE_DIR}")
target_sources(programming-network
PRIVATE send_string.c recv_line.c
PUBLIC FILE_SET HEADERS
BASE_DIRS ${PROJECT_SOURCE_DIR}
FILES publicheader1.h publicheader2.h)
install(TARGETS programming-network FILE_SET HEADERS)
现在让我们看看这些命令的作用:
add_library():定义目标的名称,静态库为STATIC,共享库为SHARED,对象为OBJECT。
target_include_directories():这里的这一行仅适用于当您有子目录和私有头文件相对于项目目录相互引用时。但通常,此命令用于在项目中包含外部头文件。
target_sources():该命令用于添加带有 PRIVATE 关键字的定义文件和私有头文件。此外,它还用于通过 FILE_SET 关键字添加公共标头。 BASE_DIRS 是把公共头的绝对路径从它们的路径中减去基目录,变成相对路径。所以这个公共标头
/home/someuser/programming-network/sub1/publicheader1.h
基本目录为
/home/someuser/programming-network/
将安装在
/cmake/install/prefix/include/sub1/publicheader.h
注意 target_sources()
也可以在子目录的 CMakeLists.txt
中使用。
install():是安装二进制文件、静态/共享库和公共头文件。默认安装子目录是 bin、lib 和 include。您也可以像这样更改
install(TARGETS myTarget
# for executables and dll on Win
RUNTIME DESTINATION bin
# shared libraries
LIBRARY DESTINATION lib
# for static libraries
ARCHIVE DESTINATION lib
# public headers
INCLUDES DESTINATION include)
最后,项目的构建和安装(对于多配置生成器:MS Visual C++、Xcode)
# in project directory
mkdir build
cd build
cmake ..
cmake --build . --config Release
cmake --install . --prefix "/usr/local/" --config Release
对于单一配置生成器(make、Ninja),删除上述 --config Release
术语并将 cmake ..
更改为 cmake -DCMAKE_BUILD_TYPE=Release ..
。
不定期副业成功案例分享
set_target_properties
的字符串中列出所有这些更聪明的解决方案?"some.h;another.h"
,请确保将变量名放在引号中。否则将导致错误或仅安装列表中的第一项。实际发生的情况取决于列表中的项目数。例如set_target_properties(myproject PROPERTIES PUBLIC_HEADER "${my_header_files}")
是正确的set_target_properties(myproject PROPERTIES PUBLIC_HEADER ${my_header_files})
不是。