如何让cmake在子文件夹中找到共享库

gro*_*190 4 c++ cmake shared-libraries

我正在尝试学习如何创建共享库。以下似乎有效(如果您对此方法有一些反馈,请发表评论,我基本上不知道我在做什么)。

在我的库项目中,我将头文件放入名为“include”的文件夹中,将源文件放入“src”中。

我的库的 CMakeLists.txt:

cmake_minimum_required(VERSION 2.4.0)

project(mycustomlib)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Include header files
include_directories(include)

# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib)

# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include)
Run Code Online (Sandbox Code Playgroud)

我的应用程序的 CMakeLists.txt:

cmake_minimum_required(VERSION 2.4.0)

project(myprogram)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})

# Find and link library
find_library(MYCUSTOMLIB mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
Run Code Online (Sandbox Code Playgroud)

这正在发挥作用。问题是我想将标头和库都放入子文件夹中(特别是:/usr/local/include/mycustomlib/标头和/usr/local/lib/mycustomlib/库)。

所以这是我的尝试:

我的库的新 CMakeLists.txt:

cmake_minimum_required(VERSION 2.4.0)

project(mycustomlib)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Include header files
include_directories(include)

# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})

# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})

# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
Run Code Online (Sandbox Code Playgroud)

我的应用程序的新 CMakeLists.txt:

cmake_minimum_required(VERSION 2.4.0)

project(myprogram)

# Find source files
file(GLOB SOURCES src/*.cpp)

# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})

# Find and link library
find_library(MYCUSTOMLIB mycustomlib/mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
Run Code Online (Sandbox Code Playgroud)

但这是行不通的。现在,我被迫像这样指定库的 .so 文件:

find_library(MYCUSTOMLIB mycustomlib/libmycustomlib.so)

怎么会?

Cra*_*ott 6

我会先处理您的实际问题,然后再提供补充意见。从技术上讲,您要求 CMake 查找名为 的库mycustomlib/mycustomlib,但您真正想说的是您想要查找mycustomlib并且可以在名为 的子目录中找到它mycustomlib。对于第二种情况,调用以实现此目的的几种替代方法find_library()是:

find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
find_library(MYCUSTOMLIB mycustomlib PATHS /usr/local/lib/mycustomlib)
Run Code Online (Sandbox Code Playgroud)

后者对库的安装位置做出了比应有的更多假设,因此我倾向于第一个选项。第一个选项假设 CMake 已经在 /usr/local/lib 中找到库,这似乎来自您的问题。您可以通过修改CMAKE_PREFIX_PATHCMAKE_LIBRARY_PATH来影响 CMake 查找库的位置。我希望上述任一选项都能使您的第二个案例发挥作用。

现在来看其他观察结果。您在每个文件的第一行请求了一个非常CMakeLists.txt旧的最低 CMake 版本。您可能至少要考虑制作 2.8(就我个人而言,我建议更像 3.2 或更高版本,但这取决于您的项目需要支持的内容)。

您已使用文件通配来获取源和标头的列表。这并不稳健,通常应该避免(请参阅此处的讨论。为了简单起见,您将看到大量示例代码使用此方法,但不建议在实际项目中使用它(CMake 文档甚至说不要使用它)。如果您想要健壮的构建,请分别明确列出您的源文件和头文件。

如果您很高兴需要 CMake 2.8.11 或更高版本(现在应该是这样),而不是调用include_directories()使所有内容都拾取您指定的标头搜索路径,您应该更愿意将搜索路径要求附加到需要的目标它。你用 来执行此操作target_include_directories()。上面的代码等效于:

target_include_directories(${PROJECT_NAME} PUBLIC include)
Run Code Online (Sandbox Code Playgroud)

随着项目规模和复杂性的增长,这可以更好地控制目标间的依赖关系。有关此主题的更深入讨论,请参阅这篇文章,也许还有这篇文章(披露:我写了两篇文章)。

您的库和程序是完全独立的源代码存储库吗?它们可以在同一个项目中构建吗?您可以在一个文件中构建多个目标CMakeLists.txt。项目名称不必与任何目标的名称有任何关系(您经常会PROJECT_NAME在简单的示例中看到变量重新用于目标名称,这是不幸的,因为它暗示了两者之间的关系,但对于除了简单的项目之外的所有项目都不会出现这种情况)。如果它们位于同一个存储库中,那么将它们构建在一起将是一个更简单的构建,因为您不必为可执行文件安装库来查找它并链接到它。

如果它们必须构建在单独的项目中,那么应用程序项目的类似以下内容应该可以帮助您完成:

cmake_minimum_required(VERSION 2.8.11)
project(myprogram)

# List your program's sources here explicitly
add_executable(myprogram src/foo.cpp src/bar.cpp)

# Find and link library
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
target_link_libraries(myprogram PUBLIC ${MYCUSTOMLIB})

# Find library's headers and add it as a search path.
# Provide the name of one header file you know should
# be present in mycustomlib's include dir.
find_path(MCL_HEADER_PATH mycustomlib.h PATH_SUFFIXES mycustomlib)
target_include_directories(myprogram PUBLIC ${MCL_HEADER_PATH})
Run Code Online (Sandbox Code Playgroud)

对于额外的点,您可以尝试通过检查公共路径前缀来确认标头路径与库位于同一区域,或者您可以通过假设目录结构从 MYCUSTOMLIB 路径派生 MCL_HEADER_PATH。两种方法都有优点和缺点。如果您想探索后者,get_filename_component()命令将是您的朋友。

希望这能为您指明正确的方向。