CMake基础 第13节 构建子项目
介绍
此示例说明如何设置包含子项目的CMake项目。顶层CMakeLists.txt调用子目录中的CMakeLists.txt以创建以下内容:
- sublibrary1 - 静态库
- sublibrary2 - 头文件库
- subbinary - 可执行文件
此示例中包含的文件包括:
$ tree
.
├── CMakeLists.txt
├── subbinary
│ ├── CMakeLists.txt
│ └── main.cpp
├── sublibrary1
│ ├── CMakeLists.txt
│ ├── include
│ │ └── sublib1
│ │ └── sublib1.h
│ └── src
│ └── sublib1.cpp
└── sublibrary2
├── CMakeLists.txt
└── include
└── sublib2
└── sublib2.h
-
[CMakeLists.txt] - 顶级CMakeLists.txt
cmake_minimum_required (VERSION 3.5) project(subprojects) # Add sub directories add_subdirectory(sublibrary1) add_subdirectory(sublibrary2) add_subdirectory(subbinary)
-
[subbinary/CMakeLists.txt] - 生成可执行文件
project(subbinary) # Create the executable add_executable(${PROJECT_NAME} main.cpp) # Link the static library from subproject1 using it's alias sub::lib1 # Link the header only library from subproject2 using it's alias sub::lib2 # This will cause the include directories for that target to be added to this project target_link_libraries(${PROJECT_NAME} sub::lib1 sub::lib2 )
-
[subbinary/main.cpp] - 可执行文件的源代码
#include "sublib1/sublib1.h" #include "sublib2/sublib2.h" int main(int argc, char *argv[]) { sublib1 hi; hi.print(); sublib2 howdy; howdy.print(); return 0; }
-
[sublibrary1/CMakeLists.txt] - 创建静态库
# Set the project name project (sublibrary1) # Add a library with the above sources add_library(${PROJECT_NAME} src/sublib1.cpp) add_library(sub::lib1 ALIAS ${PROJECT_NAME}) target_include_directories( ${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include )
-
[sublibrary1/include/sublib1/sublib1.h]
#ifndef __SUBLIB_1_H__ #define __SUBLIB_1_H__ class sublib1 { public: void print(); }; #endif
-
[sublibrary1/src/sublib1.cpp]
#include <iostream> #include "sublib1/sublib1.h" void sublib1::print() { std::cout << "Hello sub-library 1!" << std::endl; }
-
[sublibrary2/CMakeLists.txt] - 设置仅含头文件的库
# Set the project name project (sublibrary2) add_library(${PROJECT_NAME} INTERFACE) add_library(sub::lib2 ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE ${PROJECT_SOURCE_DIR}/include )
-
[sublibrary2/include/sublib2/sublib2.h]
#ifndef __SUBLIB_2_H__ #define __SUBLIB_2_H__ #include <iostream> class sublib2 { public: void print() { std::cout << "Hello header only sub-library 2!" << std::endl; } }; #endif
Tip | 在本例中,我将头文件移动到每个项目include目录下的一个子文件夹中,同时将目标include保留为根include文件夹。这是防止文件名冲突的好主意,因为你必须包含如下所示的文件:#include“subib1/subib1.h” 。这也意味着,如果你为其他用户安装库,默认安装位置将是/usr/local/include/subib1/subib1.h 。 |
---|
概念
添加子目录
CMakeLists.txt文件可以包含和调用含CMakeLists.txt的子目录
add_subdirectory(sublibrary1)
add_subdirectory(sublibrary2)
add_subdirectory(subbinary)
引用子项目目录
当使用project()命令创建项目时,CMake将自动创建许多变量,这些变量可用于引用有关项目的详细信息。然后,其他子项目或主项目可以使用这些变量。例如,引用你可以使用的不同项目的源目录。
${sublibrary1_SOURCE_DIR}
${sublibrary2_SOURCE_DIR}
CMake创建的变量包括:
Variable | Info |
---|---|
PROJECT_NAME | 由当前project() 设置的项目名称 |
CMAKE_PROJECT_NAME | 由project()命令设置的第一个项目的名称,即顶级项目 |
PROJECT_SOURCE_DIR | 当前项目的源目录 |
PROJECT_BINARY_DIR | 当前项目的生成目录 |
name_SOURCE_DIR | 名为“name”的项目的源目录。在本例中,创建的源目录将是sublibrary1_SOURCE_DIR 、sublibrary2_SOURCE_DIR 和subbinary_SOURCE_DIR |
name_BINARY_DIR | 名为“name”的项目的二进制目录。在本例中,创建的二进制目录为sublibrary1_BINARY_DIR 、sublibrary2_BINARY_DIR 和subbinary_BINARY_DIR 。 |
头文件库
如果你有一个被创建为只包含头文件的库,cmake支持接口目标,以允许在没有任何构建输出的情况下创建目标。有关更多详细信息,请单击此处。
add_library(${PROJECT_NAME} INTERFACE)
在创建目标时,你还可以使用INTERFACE作用域包含该目标的目录。INTERFACE作用域用于制定目标要求,这些要求在链接此目标的任何库中使用,但不用于目标本身的编译。如下示例,链接至此目标的任何目标都将包含一个include目录,但此目标本身并不进行编译(即不产生任何实体内容):
target_include_directories(${PROJECT_NAME}
INTERFACE
${PROJECT_SOURCE_DIR}/include
)
从子项目中引用库
如果某个子项目创建库,则其他项目可以通过在target_link_library()
命令中调用该项目的名称来引用该库。这意味着你不必引用新库的完整路径,它将作为依赖项被添加。
target_link_libraries(subbinary
PUBLIC
sublibrary1
)
或者,你可以创建一个别名目标,使你可以在只读上下文中引用该目标。
要创建别名目标运行,请执行以下操作:
add_library(sublibrary2)
add_library(sub::lib2 ALIAS sublibrary2)
要引用别名,只需如下所示:
target_link_libraries(subbinary
sub::lib2
)
包含来自子项目的头文件目录
当从子项目添加库时,从cmake v3
开始,不需要使用它们在二进制文件的include目录中添加项目include目录。
这由创建库时target_include_directory()
命令中的作用域控制。在本例中,因为子二进制可执行文件链接了subibrary1和subibrary2库,所以它将自动包括${subibrary1_source_DIR}/include
和${subibrary2_source_DIR}/include
文件夹,因为它们是随库的PUBLIC和INTERFACE范围导出的。
构建示例
$ mkdir build
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matrim/workspace/cmake-examples/02-sub-projects/A-basic/build
$ make
Scanning dependencies of target sublibrary1
[ 50%] Building CXX object sublibrary1/CMakeFiles/sublibrary1.dir/src/sublib1.cpp.o
Linking CXX static library libsublibrary1.a
[ 50%] Built target sublibrary1
Scanning dependencies of target subbinary
[100%] Building CXX object subbinary/CMakeFiles/subbinary.dir/main.cpp.o
Linking CXX executable subbinary
[100%] Built target subbinary