嵌套式CMakeLists写法,文件结构如下:
|--examples
| |--add_example.c
| |--sub_example.c
|--CMakeLists.txt
|--add
| |--add.c
| |--add.h
|--sub
| |--sub.c
| |--sub.h
文件内容详情见C/C++开发之CMakelist(其一)
将C/C++开发之CMakeList(其一)的内容推广,我们可以将对应文件相对路径分别记录,再统一编译运行
需要注意添加include_directories,指定include目录,否则在add.c与sub.c中可能找不到头文件。
#最小cmake版本
cmake_minimum_required(VERSION 2.8)
#项目名字,随意起
project(simple_exe)
#设置C/c++版本(如c99,c++11,c++17等版本),下面表示使用c99版本
set(CMAKE_C_STANDARD 99)
#指定include路径
#${CMAKE_CURRENT_SOURCE_DIR}表示当前文件目录,是cmake内置变量
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/add/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sub/)
#set作用为将第一个字符之后的字符命名为第一个字符,
#下面的例子表示将add.c与sub.c用SOURCES表示,即SOURCES = [add.c, sub.c]
set(SOURCES
add/add.c
sub/sub.c
)
#将可执行文件统一命名为MAIN_FILES
set(MAIN_FILES
examples/add_example.c
examples/sub_example.c
)
#将文件打包成alg库,中间的选项STATIC表示静态库,${SOURCES}为源文件,alg为生成的静态库名字
add_library(alg STATIC ${SOURCES})
#循环MAIN_FILES中的每一个元素
foreach(mainfile ${MAIN_FILES})
#获取不带文件后缀的文件名,下面的作用为提取add_example与sub_example
get_filename_component(mainname ${mainfile} NAME_WE)
#将mainfile编译生成可执行文件mainname
add_executable(${mainname} ${mainfile})
#将静态库链接到可执行文件中
target_link_libraries(${mainname} alg)
endforeach()
但是头文件过多时,写起来不方便,用户调用也不方便,最好的方式是将所有头文件整合到一起,统一加到alg.h中,文件结构如下:
|--examples
| |--add_example.c
| |--sub_example.c
|--CMakeLists.txt
|--add
| |--add.c
|--sub
| |--sub.c
|--include
| |--alg.h
头文件alg.h:
#ifndef __ALG_H
#define __ALG_H
int my_sub(int a, int b);
int my_add(int a, int b);
#endif
源文件add.c,sub.c
add.c
#include "alg.h"
int my_add(int a, int b){
return a + b;
}
sub.c
#include "alg.h"
int my_sub(int a, int b){
return a - b;
}
main文件sub_example.c, add_example.c
add_example.c
#include "alg.h"
#include
int main(){
int a = 1, b = 2;
printf("add:%d\n", my_add(a, b));
return 0;
}
sub_example.c
#include "alg.h"
#include
int main(){
int a = 1, b = 2;
printf("sub:%d\n", my_sub(a, b));
return 0;
}
对应cmake也需要改写include_directories
#最小cmake版本
cmake_minimum_required(VERSION 2.8)
#项目名字,随意起
project(simple_exe)
#设置C/c++版本(如c99,c++11,c++17等版本),下面表示使用c99版本
set(CMAKE_C_STANDARD 99)
#指定include目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
#set作用为将第一个字符之后的字符命名为第一个字符,
#下面的例子表示将add.c与sub.c用SOURCES表示,即SOURCES = [add.c, sub.c]
set(SOURCES
add/add.c
sub/sub.c
)
#将可执行文件统一命名为MAIN_FILES
set(MAIN_FILES
examples/add_example.c
examples/sub_example.c
)
#将文件打包成alg库,中间的选项STATIC表示静态库,${SOURCES}为源文件,alg为生成的静态库名字
add_library(alg STATIC ${SOURCES})
#循环MAIN_FILES中的每一个元素
foreach(mainfile ${MAIN_FILES})
#获取不带文件后缀的文件名,下面的作用为提取add_example与sub_example
get_filename_component(mainname ${mainfile} NAME_WE)
#将mainfile编译生成可执行文件mainname
add_executable(${mainname} ${mainfile})
#将静态库链接到可执行文件中
target_link_libraries(${mainname} alg)
endforeach()
如果源文件也特别多,文件夹里面也嵌套了很多文件夹,将文件中所有目录全写入最外层CMakeLists显得特别混乱,不利于团队开发。解决方案为在合适的文件嵌套深度写CMakelists.txt文件,然后将内部的CMakelists.txt文件中的变量层层传入外部合适的位置。不慌,看案例:
我们再增加一层目录,如下
|--examples
| |--add_example.c
| |--sub_example.c
|--CMakeLists.txt
|--include
| |--alg.h
|--alg
| |--add
| | |--add.c
| | |--CMakeLists.txt
| |--sub
| | |--sub.c
| | |--CMakeLists.txt
| |--CMakeLists.txt
alg/add/CMakeLists.txt中重点为set(SRCS ${SRCS} ${SOURCES} PARENT_SCOPE),表示作用域在父目录
#老三段
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
#注意这里的目录不是相对路径,是从可执行CMakeLists.txt开始的(即存在add_library或add_executable的CMakeLists,如果写错的话会报错找不到改文件,可以考虑修改一下子目录里面的CMakeLists中SOURCES的目录)
set(SOURCES
alg/add/add.c
)
#等价于SRC = [SRC, SOURCES],PARENT_SCOPE表示作用域为父目录
#注意这里为啥不写成set(SOURCES ${SOURCES} ${SOURCES} PARENT_SCOPE),注意到第8行的set(SOURCES ...)
#如果写成这种形式,如果在其它层也存在SOURCES,那么在第八行SOURCES会被替换成[add/add.c, sub/sub.c]导致找不到部分源文件
#所以在该项目里我们约定SRCS为全局变量,提供给外层使用,SOURCES仅在本层使用
set(SRCS ${SRCS} ${SOURCES} PARENT_SCOPE)
alg/sub/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
set(SOURCES
alg/sub/sub.c
)
set(SRCS ${SRCS} ${SOURCES} PARENT_SCOPE)
alg/CMakeLists.txt如下,重点为add_subdirectory,表示调用子目录的CMakeLists:
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
#将文件夹内存在CMakelist的所有文件命名为MODULES
set(MODULES
add
sub
)
foreach(module ${MODULES})
#执行每一个文件夹里面的CMakeLists.txt脚本
add_subdirectory(${module})
endforeach()
#将SRC传入上层,这样可以层层上传子文件夹中CMakeLists里的变量。
set(SRCS ${SRCS} PARENT_SCOPE)
CMakeLists.txt如下:
#最小cmake版本
cmake_minimum_required(VERSION 2.8)
#项目名字,随意起
project(simple_exe)
#设置C/c++版本(如c99,c++11,c++17等版本),下面表示使用c99版本
set(CMAKE_C_STANDARD 99)
#指定include目录
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
#将文件夹内存在CMakelist的所有文件命名为MODULES
set(MODULES
alg
)
foreach(module ${MODULES})
#执行每一个文件夹里面的CMakeLists.txt脚本
add_subdirectory(${module})
endforeach()
#将可执行文件统一命名为MAIN_FILES
set(MAIN_FILES
examples/add_example.c
examples/sub_example.c
)
#将文件打包成alg库,中间的选项STATIC表示静态库,${SOURCES}为源文件,alg为生成的静态库名字
add_library(alg STATIC ${SRCS})
#循环MAIN_FILES中的每一个元素
foreach(mainfile ${MAIN_FILES})
#获取不带文件后缀的文件名,下面的作用为提取add_example与sub_example
get_filename_component(mainname ${mainfile} NAME_WE)
#将mainfile编译生成可执行文件mainname
add_executable(${mainname} ${mainfile})
#将静态库链接到可执行文件中
target_link_libraries(${mainname} alg)
endforeach()
但是!!!如果main示例文件过多而且也存在各种文件嵌套,怎么办?
修改文件目录如下:
|--examples
| |--add_example.c
| |--sub_example.c
| |--CMakeLists.txt
|--CMakeLists.txt
|--include
| |--alg.h
|--alg
| |--add
| | |--add.c
| | |--CMakeLists.txt
| |--sub
| | |--sub.c
| | |--CMakeLists.txt
| |--CMakeLists.txt
|--build
examples/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(simple_exe)
set(CMAKE_C_STANDARD 99)
#将可执行文件统一命名为MAIN_FILES
set(MAIN_FILES
add_example.c
sub_example.c
)
#循环MAIN_FILES中的每一个元素
foreach(mainfile ${MAIN_FILES})
#获取不带文件后缀的文件名,下面的作用为提取add_example与sub_example
get_filename_component(mainname ${mainfile} NAME_WE)
#将mainfile编译生成可执行文件mainname
add_executable(${mainname} ${mainfile})
#将静态库链接到可执行文件中
target_link_libraries(${mainname} alg)
endforeach()
alg/add/CMakeLists.txt
#老三段
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
#对比一下上一节SOURCES的写法
set(SOURCES
add/add.c
)
set(SRCS ${SRCS} ${SOURCES} PARENT_SCOPE)
alg/sub/CMakeLists.txt
#老三段
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
set(SOURCES
sub/sub.c
)
set(SRCS ${SRCS} ${SOURCES} PARENT_SCOPE)
alg/CMakeLists.txt,重点理解一下SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/…/lib)
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
#设置LIBRARY_OUTPUT_PATH为库的输出位置
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
#将文件夹内存在CMakelist的所有文件命名为MODULES
set(MODULES
add
sub
)
foreach(module ${MODULES})
#执行每一个文件夹里面的CMakeLists.txt脚本
add_subdirectory(${module})
endforeach()
#将文件打包成alg库,中间的选项STATIC表示静态库,${SOURCES}为源文件,alg为生成的静态库名字
add_library(alg STATIC ${SRCS})
CMakeLists.txt
#最小cmake版本
cmake_minimum_required(VERSION 2.8)
#项目名字,随意起
project(simple_exe)
#设置C/c++版本(如c99,c++11,c++17等版本),下面表示使用c99版本
set(CMAKE_C_STANDARD 99)
#指定include目录,作用域为全局,包括子目录中的CMakeLists
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
#指定静态库输出路径,作用域为全局,包括子目录中的CMakeLists
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib)
#将文件夹内存在CMakelist的所有文件命名为MODULES
set(MODULES
alg
examples
)
foreach(module ${MODULES})
#执行每一个文件夹里面的CMakeLists.txt脚本
add_subdirectory(${module})
endforeach()
查看一下编译之后的文件目录,对比一下编译之前的目录
|--examples
| |--add_example.c
| |--sub_example.c
| |--CMakeLists.txt
|--CMakeLists.txt
|--include
| |--alg.h
|--alg
| |--add
| | |--add.c
| | |--CMakeLists.txt
| |--sub
| | |--sub.c
| | |--CMakeLists.txt
| |--CMakeLists.txt
|--lib
| |--libalg.a
还差一步就可以打包发布自己的程序啦,回忆一下我们源码安装c/c++程序时,离不开这几个命令,cmake, make , make install。目前我们仅仅在使用cmake, make之后在build/examples里面可以找到自己的源码,所以如何使用make install呢?
make install发布自己的程序
只需要替换上一节中alg/CmkaeLists.txt的内容如下,注意Install命令
#老三段
cmake_minimum_required(VERSION 2.8)
project(simple_alg_exe)
set(CMAKE_C_STANDARD 99)
#设置LIBRARY_OUTPUT_PATH为库的输出位置
SET(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
#将文件夹内存在CMakelist的所有文件命名为MODULES
set(MODULES
add
sub
)
foreach(module ${MODULES})
#执行每一个文件夹里面的CMakeLists.txt脚本
add_subdirectory(${module})
endforeach()
#将文件打包成alg库,中间的选项STATIC表示静态库,${SOURCES}为源文件,alg为生成的静态库名字
add_library(alg STATIC ${SRCS})
#将alg.h头文件发布到${CMAKE_CURRENT_SOURCE_DIR}/../output/include目录中,当然可以写一些其它的目录,如/usr/local/lib64/include
install(FILES ../include/alg.h DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../output/include)
#将libalg.a静态库文件发布到${CMAKE_CURRENT_SOURCE_DIR}/../output/lib目录中
install(FILES ../lib/libalg.a DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/../output/lib)
当输入make install时输出如下:
make install
[ 42%] Built target alg
[ 71%] Built target sub_example
[100%] Built target add_example
Install the project...
-- Install configuration: ""
-- Installing: /home/lichangshi/ML2/my_test/test_cmake/alg/../output/include/alg.h
-- Installing: /home/lichangshi/ML2/my_test/test_cmake/alg/../output/lib/libalg.a
安装成功,查看目录结构
|--examples
| |--add_example.c
| |--sub_example.c
| |--CMakeLists.txt
|--CMakeLists.txt
|--include
| |--alg.h
|--alg
| |--add
| | |--add.c
| | |--CMakeLists.txt
| |--sub
| | |--sub.c
| | |--CMakeLists.txt
| |--CMakeLists.txt
|--output
| |--lib
| | |--libalg.a
| |--include
| | |--alg.h
|--lib
| |--libalg.a
安装成功,其它程序只要正确找到对应的头文件与静态库就可以直接调用自己的程序。
C语言开发之CMakeLists以及gtest测试在C语言中的使用方法(其四)
C语言开发之CMakeList(其三)
C/C++开发之CMakeList(其二)
C/C++开发之CMakeLists(其一)