2014-12-27 50 views
3

我有一个用C++编写的项目,我正在使用cmake来构建它。 该项目有许多子项目,其中一个是其他子项目所需的lib。 我可以编译和使用 add_library移动。所以到构建目录,并 INSTALL(靶向...)cmake install(FILES ...)似乎没有工作

但是我还需要的lib的头文件的包含目录下安装建立目录。 我使用安装(文件...)来做到这一点,但它似乎根本没有做到这一点。

为了演示,我创建通过qtcreator一个测试项目,

& ls test 
CMakeLists.txt empty.hh main.cpp 

$ cat test/CMakeLists.txt 
project(test) 
cmake_minimum_required(VERSION 2.8) 
install(FILES empty.hh DESTINATION include) 
aux_source_directory(. SRC_LIST) 
add_executable(${PROJECT_NAME} ${SRC_LIST}) 

$ cat test/main.cpp 
#include 

using namespace std; 

int main() 
{ 
cout << "Hello World!" << endl; 
return 0; 
} 

$ cat test/empty.hh 
#ifndef EMPTY_HH 
#define EMPTY_HH 

#endif // EMPTY_HH 

If the files under "test" qtcreator will compile (by default) the files to test-build. 
$ ls test-build/ 
CMakeCache.txt CMakeFiles Makefile cmake_install.cmake test test.cbp 
$ ./test-build/test 
Hello World! 

正如你所看到的,没有包含目录或文件empty.hh。 也试图使用

install(FILES empty.hh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 

但仍然看不到头文件。

$ cmake --help 
cmake version 2.8.12 

如果您有任何想法,请让我知道。

回答

8

使用install命令在构建时移动文件通常是不好的方法。该命令旨在用于设置在用户执行make install或同等操作时将安装的文件和目标。由于你没有运行make install,我预计这就是为什么install(FILES ...)命令似乎不起作用。

你有一些稍微不同的方法来完成这项工作。

我推荐不是如果您不需要移动标题。假设你的库称为MyLib,那么你可以通过target_include_directories使这些头可作为MyLib目标的一部分:

target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") 

这意味着,如果有一个相关的目标,如MyExe

target_link_libraries(MyExe MyLib) 

则自动有权访问MyLib的源代码目录。


如果库的目录结构不适合将API头与其余源和头分开,那么这可能不太理想。假设MyLib由以下文件组成:my_lib.cpp,my_lib_api.hh(API标头 - 用于其他项目)和my_lib_detail.hh(不包含在其他项目中)。一个理想的结构将保持API标题与其余的不同,例如,

/my_lib 
    - CMakeLists.txt 
    - src/ 
     - my_lib.cpp 
     - my_lib_detail.hh 
    - include/ 
     - my_lib/ 
      - my_lib_api.hh 

有了这个结构,你可以只指定

target_include_directories(MyLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" 
           PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") 

MyLib来源将能够遏制#include "my_lib_detail.hh"#include "my_lib/my_lib_api.hh",但依赖的目标的来源只能包含#include "my_lib/my_lib_api.hh"


所以,如果MyLib刚刚平面结构或不内部接头从API孩子们分开,你可能希望将API头的位置复制了在构建树,并添加到路径致电target_include_directories。在这种情况下,如果MyLib本身不需要访问复制的文件(只是源树中的原始文件),则可以在调用target_include_directories时使用INTERFACE而不是PUBLIC。但是,这个问题的关键(并回答你的实际问题我想)是作为配置过程(当CMake运行时)或构建过程(当运行时)的一部分复制这些文件 - 而不是作为部分的安装过程。

所以,让我们假设MyLib具有上文所示的有用的目录结构,而是是平坦的(即和的CMakeLists.txt三个源文件都在相同的目录)。我们可以使用一个生成后命令API头复制出来,以构建树:

project(my_lib) 
cmake_minimum_required(VERSION 2.8.12.2) # for 'target_include_directories' 
add_library(MyLib SHARED my_lib.cpp my_lib_api.hh my_lib_detail.hh) 
target_include_directories(MyLib INTERFACE "${CMAKE_BINARY_DIR}/include" 
           PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") 

file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/include/my_lib") 
add_custom_command(TARGET MyLib POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy_if_different 
     "${CMAKE_CURRENT_SOURCE_DIR}/my_lib_api.hh" 
     "${CMAKE_BINARY_DIR}/include/my_lib" 
    COMMENT "Copying MyLib public headers to ${CMAKE_BINARY_DIR}/include/my_lib" 
    VERBATIM) 

那么对于MyExe的的CMakeLists.txt,你可以做:

project(my_exe) 
cmake_minimum_required(VERSION 2.8.12.2) 
add_executable(MyExe main.cpp) 
target_link_libraries(MyExe MyLib) 

# Copy MyLib.so to this build dir to allow MyExe to find it at runtime 
add_custom_command(TARGET MyExe POST_BUILD 
    COMMAND ${CMAKE_COMMAND} -E copy_if_different 
     "$<TARGET_FILE:MyLib>" 
     "$<TARGET_FILE_DIR:MyExe>" 
    VERBATIM) 


这不会像MSVC那样工作 - 您需要处理Windows导出库(有关更多详细信息,请参见CMake wikiGenerateExportHeader的文档),但它只是涉及诸如将以下内容添加到MyLib的的CMakeLists.txt:

include(GenerateExportHeader) 
generate_export_header(MyLib 
    BASE_NAME MyLib 
    EXPORT_MACRO_NAME MyLib_EXPORT 
    EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/include/my_lib/my_lib_export.hh" 
    STATIC_DEFINE MyLib_BUILT_AS_STATIC) 

如果你确实需要这样,你必须改变INTERFACEPUBLICtarget_include_directories(MyLib ...),因为MyLib然后将自己需要访问生成的文件"${CMAKE_BINARY_DIR}/include/my_lib/my_lib_export.hh"

+0

嗨弗雷泽,谢谢你。不幸的是,我不能试试这个,因为我没有cmake 2.8.12.2。但是你的解决方案似乎很有前途,我会在稍后尝试。在此之前,我将使用文件(COPY)将头文件复制到所需位置。再次感谢! – czappa

+0

不用担心 - '文件(COPY ...)'应该可以正常工作。 – Fraser