linux下使用CMake编译项目工程
简介
CMake 是一个跨平台的项目构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出 Makefile 或者 vs项目文件,CMake 并不直接建构出最终的软件,而是产生标准的建构档(如 Makefile 或 vs项目文件)
本文讨论linux下编译。
目录结构
项目主目录存在一个CMakeLists.txt文件,两种目录结构方式:
1.子文件夹下未含有CMakeLists.txt,主目录将子目录所有编译的规则写到主目录的CMakeLists.txt中。
2.子文件夹下含有CMakeLists.txt文件,主目录通过添加子目录方式。
编译方式
cd到CMakeLists.txt所在主目录
1.内部编译
cmake ./
make
2.外部编译
mkdir build
cd build
cmake ../
make
常用语法
demo
├── CMakeLists.txt
├── hello.cpp
└── testcmake.cmake
testcmake.cmake
function(test_func src_list)
message("1111 ${src_list}")
list(APPEND src_list a)
message("1111 ${src_list}")
list(LENGTH src_list len)
if(len LESS 10)
test_func("${src_list}")
endif()
set( src_list ${src_list} PARENT_SCOPE)
endfunction()
function(test_func_1 src_list)
message("1111 ${src_list}")
list(APPEND ${src_list} a)
message("1111 ${src_list}")
list(LENGTH ${src_list} len)
if(len LESS 10)
test_func("${src_list}")
endif()
set( ${src_list} ${${src_list}} PARENT_SCOPE)
endfunction()
CMakeLists.txt
#设置CMake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
#设置项目名和支持语言(语言可以不用设置)
PROJECT(hello C CXX)
LIST(APPEND CMAKE_MODULE_PATH
"${PROJECT_SOURCE_DIR}/")
include(testcmake)
#打印环境变量
message (STATUS ${PROJECT_SOURCE_DIR}) # 工程的根目录
message (STATUS ${PROJECT_BINARY_DIR}) # 运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/build
message (STATUS ${CMAKE_CURRENT_SOURCE_DIR}) # 当前处理的CMakeLists.txt所在的路径
message (STATUS ${CMAKE_CURRENT_BINARY_DIR}) # target编译目录
message (STATUS ${CMAKE_CURRENT_LIST_FILE}) # 输出调用这个变量的CMakeLists.txt的完整路径
message (STATUS ${PROJECT_NAME}) # 返回通过PROJECT指令定义的项目名称
message(STATUS "operating system: ${CMAKE_SYSTEM_NAME}")
#判断系统
IF(WIN32)
# 打印
message(STATUS "do something related to ${CMAKE_SYSTEM_NAME}")
ELSEIF(UNIX)
message(STATUS "do something related to ${CMAKE_SYSTEM_NAME}")
ELSEIF(APPLE)
message(STATUS "do something related to ${CMAKE_SYSTEM_NAME}")
ELSEIF(LINUX)
message(STATUS "do something related to ${CMAKE_SYSTEM_NAME}")
ENDIF(WIN32)
# 添加编译参数
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-std=c++11)
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
# 设置GCC G++(如重新装g++ 低版本linux系统g++不支持c11需要另装)
# SET(CMAKE_C_COMPILER "/usr/local/bin/gcc")
# SET(CMAKE_CXX_COMPILER "/usr/local/bin/g++")
# 设置头文件路径
INCLUDE_DIRECTORIES(
${PROJECT_SOURCE_DIR}/
${PROJECT_SOURCE_DIR}/../
/usr/include/mysql/
)
#调用函数
test_func(src_list)
message(">>>>>>> ${src_list}")
test_func_1(src_list_1)
message(">>>>>>> ${src_list_1}")
#for循环
foreach( f ${src_list_1})
message(STATUS ${f})
endforeach()
# 设置安装路径
SET(APP_INSTALL_PATH "${PROJECT_SOURCE_DIR}/bin")
# 添加宏定义
add_definitions(-DNDEBUG)
# 设置c++编译参数
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -fmessage-length=0")
# 设置生成可执行程序和编译规则
ADD_EXECUTABLE(hello hello.cpp)
# 设置安装规则
INSTALL(TARGETS hello DESTINATION ${APP_INSTALL_PATH})
生成静态库,只需要将 ADD_EXECUTABLE替换成ADD_LIBRARY
实例
单目录单文件项目 (demo0)
├── CMakeLists.txt
└── hello.cpp
# 设置CMake最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 设置项目名 并设置只支持C和C++编译(可以不设置)
PROJECT(hello C CXX)
# 设置编译规则(生成可执行文件)
ADD_EXECUTABLE(hello hello.cpp)
单目录多文件编译(demo1)
├── CMakeLists.txt
├── hello.cpp
├── test.cpp
└── test.h
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello C CXX)
ADD_EXECUTABLE(hello hello.cpp test.cpp)
如果这时候有n个cpp,每个都需要写上去,就要每次都要维护CMakeLists.txt,所以就有下面的写法(demo2)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello C CXX)
# 将源文件遍历到一个list中
AUX_SOURCE_DIRECTORY(./ SOURCE_LIST)
# 统一添加一个list
ADD_EXECUTABLE(hello ${SOURCE_LIST})
多目录多文件编译
1.主目标CMakeLists.txt包含子目录编译规则(demo3)
├── CMakeLists.txt
├── hello.cpp
└── test
├── test.cpp
└── test.h
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello)
# 引入头文件目录
include_directories(test)
AUX_SOURCE_DIRECTORY(./ SOURCE_LIST)
AUX_SOURCE_DIRECTORY(./test TEST_SOURCE_LIST)
ADD_EXECUTABLE(hello ${SOURCE_LIST} ${TEST_SOURCE_LIST})
2.主目标CMakeLists.txt包含子目录编译规则(demo4)
├── CMakeLists.txt
├── hello.cpp
└── test
├── CMakeLists.txt
├── test.cpp
└── test.h
主目录的CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(hello)
include_directories(test)
AUX_SOURCE_DIRECTORY(./ SOURCE_LIST)
# 添加子目录
ADD_SUBDIRECTORY(test)
ADD_EXECUTABLE(hello ${SOURCE_LIST})
TARGET_LINK_LIBRARIES(hello testLib)
test文件夹下的CMakeLists.txt
AUX_SOURCE_DIRECTORY(. TEST_SOURCE_LIST)
ADD_LIBRARY(testLib ${TEST_SOURCE_LIST})
多项目多层目录编译
实际项目中,不可能遇到这么简单的结构。
按照两种目录结构方式,第一种需要遍历所有的源文件加到CMakeLists.txt中,第二种就需要在所有字目录中添加CMakeLists.txt,这样版本迭代时候,这样每个子目录下都会生成CMakeLists.txt,显然不符合我们的实际需求。
按照第一种方式我们只需要把源文件遍历就行了,然后加到 ADD_EXECUTABLE 或者 ADD_LIBRARY就行了,紧接着我根据这个思路写了一个Python脚本。。。
1.通过python生成CMakeLists.txt 适合CMake一点都不懂的
通过配置cmake.config来配置相关库头文件等等(demo5)
├── cmakelist.py
├── engine
│ ├── cmake.config
│ ├── CMakeLists.txt
│ ├── config.h
│ ├── gameapp
│ │ ├── gameapp.cpp
│ │ └── gameapp.h
│ ├── json
.......
│ └── util
│ └── shared.h
├── loginapp
│ ├── cmake.config
│ ├── CMakeLists.txt
│ ├── loginapp.cpp
│ ├── loginapp.h
│ ├── main.cpp
│ └── module
│ ├── loginhelper.cpp
│ └── loginhelper.h
└── testclient
├── cmake.config
├── CMakeLists.txt
├── main.cpp
├── testclient.cpp
└── testclient.h
cmakelist.py
# -*- coding: utf-8 -*-
import os
import sys
import demjson
enter_str = '\n'
desc_str = '''
(1).type: exe or lib
(2).g++ gcc 可以不填,由于Centos 6默认g++不支持c++11,需要另外装g++
(3).如果想忽略某个文件夹下在下面建一个cmake.ignore
(4).output_path相对项目路径的
(5).lib:库之间有先有顺序,被依赖的库放到后面 '''
temp_str = '''
{
"cmake_version":"2.8.12.2",
"gcc":"/usr/local/bin/gcc",
"g++":"/usr/local/bin/g++",
"type": "exe",
"project":"GateApp",
"include_path":"./ ../ ./mysql /usr/include/mysql/ ../../../3rd",
"lib_path": "/usr/lib64/mysql/ ../../../lib ../../../3rd/lib",
"lib": "ACE z cppunit uuid Common GameConfigData Common Dao Message engine",
"output_path": "../../../App",
"options":"-DNDEBUG -O3 -Wall -c -fmessage-length=0 -std=c++11 -fpermissive -lpthread"
}
'''
def write_file_context(dir_path, file_name, context):
file_path = os.path.join(dir_path, file_name)
temp_file = open(file_path, "w")
context += '\n'
temp_file.write(context)
temp_file.close()
ignore_dirs = []
def walk_and_modify_cpp(path):
path_src = ''
for r, dirs, files in os.walk(path):
cmake_ignore = os.path.join(r, "cmake.ignore")
if os.path.isfile(cmake_ignore) or r in ignore_dirs:
for dd in dirs:
new_dd = os.path.join(r, dd)
if new_dd in ignore_dirs:
continue
ignore_dirs.append(new_dd)
print(new_dd)
continue
for f in files:
if f.endswith(".cpp") or f.endswith(".c") or f.endswith(".pb.cc"):
new_path = os.path.join(r, f)
sp_ret = new_path.split(path, 1)
if len(sp_ret) != 2:
print(r)
print(f)
print(new_path)
print(sp_ret)
new_path_src = sp_ret[1]
if new_path_src[0] == '/':
new_path_src = "." + new_path_src
else:
new_path_src = os.path.join(".", new_path_src)
path_src += new_path_src + enter_str
return path_src
def main():
if len(sys.argv) < 2:
exit("argv num != 2")
dir_name = sys.argv[1]
if not os.path.isdir(dir_name):
exit("{0} not exist!".format(dir_name))
json_file = os.path.join(dir_name, "cmake.config")
if not os.path.isfile(json_file):
print(desc_str)
print(temp_str)
exit("{0} not exist!".format(json_file))
js = demjson.decode_file(json_file)
project_name = js["project"]
exe_type = js["type"]
include_path = js['include_path']
lib_path = js['lib_path']
lib = js['lib']
output_path = js['output_path']
if exe_type not in ['exe', 'lib']:
exit("type error in cmake.config")
cmake = "cmake_minimum_required(VERSION {0})".format(js["cmake_version"]) + enter_str
if "gcc" in js and js["gcc"] != "":
cmake += '''SET(CMAKE_C_COMPILER "{0}")'''.format(js["gcc"]) + enter_str
if "g++" in js and js["g++"] != "":
cmake += '''SET(CMAKE_CXX_COMPILER "{0}")'''.format(js["g++"]) + enter_str
cmake += "project({0})".format(project_name) + enter_str
if include_path != "":
cmake += "INCLUDE_DIRECTORIES({0})".format(include_path) + enter_str
if lib_path != "":
cmake += "LINK_DIRECTORIES({0})".format(lib_path) + enter_str
if output_path != "":
if exe_type == "exe":
cmake += "SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/"
elif exe_type == "lib":
cmake += "SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/"
cmake += "{0})".format(output_path) + enter_str
cmake += '''SET(CMAKE_BUILD_TYPE "Release")''' + enter_str
cmake += "ADD_DEFINITIONS({0})".format(js['options']) + enter_str
new_path_src = walk_and_modify_cpp(dir_name)
if exe_type == "exe":
cmake += "ADD_EXECUTABLE({0} ".format(project_name)
elif exe_type == "lib":
cmake += "ADD_LIBRARY({0} ".format(project_name) + enter_str
cmake += new_path_src
cmake += ")" + enter_str
if lib != "":
cmake += "TARGET_LINK_LIBRARIES({0} {1})".format(project_name, lib) + enter_str
write_file_context(dir_name, "CMakeLists.txt", cmake)
print(cmake)
if __name__ == "__main__":
main()
cmake.config (只贴了loginapp的)
{
"cmake_version":"2.8.12.2",
"gcc":"",
"g++":"",
"type": "exe",
"project":"loginapp",
"include_path":"./ ../ ./mysql /usr/include/mysql/",
"lib_path": "../lib",
"lib": "engine event event_pthreads protobuf tcmalloc_minimal pthread",
"output_path": "../bin",
"options":"-DNDEBUG -O3 -Wall -c -fmessage-length=0 -std=c++11 -fpermissive -lpthread"
}
cd demo5
python cmakelist.py engine
mkdir build
cd build
mkdir engine
cd engine
cmake ../../engine
make
然后执行python cmakelist.py engine(文件夹名)来生成CMakeLists.txt
对于上面方式,就是在cmake外层又包了一层python,这样就像套娃中的套娃。。。
2.使用CMake函数
直到看到CMake可以通过函数找到源文件,果断放弃python的方式
├── AutoCollect.cmake
├── CMakeLists.txt
├── engine
│ ├── CMakeLists.txt
│ ├── config.h
│ ├── gameapp
│ │ ├── gameapp.cpp
│ │ └── gameapp.h
│ ├── json
│ │ ├── allocator.h
│ │ ├── assertions.h
.....
│ ├── timer
....
│ │ └── timerutil.h
│ └── util
│ ├── autolock.cpp
....
│ └── shared.h
├── loginapp
│ ├── CMakeLists.txt
│ ├── loginapp.cpp
│ ├── loginapp.h
│ ├── main.cpp
│ └── module
│ ├── loginhelper.cpp
│ └── loginhelper.h
└── testclient
├── main.cpp
├── testclient.cpp
└── testclient.h
AutoCollect.cmake
function(CollectSourceFiles current_dir variable)
list(FIND ARGN "${current_dir}" IS_EXCLUDED)
if(IS_EXCLUDED EQUAL -1)
file(GLOB COLLECTED_SOURCES
${current_dir}/*.c
${current_dir}/*.cc
${current_dir}/*.cpp
${current_dir}/*.inl
${current_dir}/*.def
${current_dir}/*.h
${current_dir}/*.hh
${current_dir}/*.hpp)
list(APPEND ${variable} ${COLLECTED_SOURCES})
file(GLOB SUB_DIRECTORIES ${current_dir}/*)
foreach(SUB_DIRECTORY ${SUB_DIRECTORIES})
if (IS_DIRECTORY ${SUB_DIRECTORY} AND NOT(${SUB_DIRECTORY} MATCHES ".svn"))
CollectSourceFiles("${SUB_DIRECTORY}" "${variable}" "${ARGN}")
endif()
endforeach()
set(${variable} ${${variable}} PARENT_SCOPE)
endif()
endfunction()
主目录下的CMakaLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(gameproject)
SET(APP_INSTALL_PATH "${PROJECT_SOURCE_DIR}/../bin")
# set macro-directory
LIST(APPEND CMAKE_MODULE_PATH
"${PROJECT_SOURCE_DIR}/")
INCLUDE(AutoCollect)
INCLUDE_DIRECTORIES(
${PROJECT_SOURCE_DIR}/
/usr/include/mysql/
)
LINK_DIRECTORIES(
${PROJECT_SOURCE_DIR}/../../lib/
)
ADD_SUBDIRECTORY(engine)
ADD_SUBDIRECTORY(loginapp)
engine目录下的CMakeLists.txt
CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}
SRC_LIST)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -fmessage-length=0 -std=c++11")
add_definitions(-DNDEBUG)
ADD_LIBRARY(engine STATIC ${SRC_LIST})
loginapp目录下的CMakeLists.txt
CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}
SRC_LIST)
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall -fmessage-length=0 -std=c++11")
add_definitions(-DNDEBUG)
ADD_EXECUTABLE(loginapp ${SRC_LIST})
TARGET_LINK_LIBRARIES(loginapp engine event event_pthreads pthread)
INSTALL(TARGETS loginapp DESTINATION ${APP_INSTALL_PATH})
testclient 是用测试的,就没写了。。。
相关推荐
-
PHP8种变量类型的详细讲解2025-02-22 00:32:24
-
php+apache 和 php+nginx的区别2025-02-22 00:21:27
-
PHP:与workerman结合实现定时任务2025-02-22 00:15:57
-
Nginx的Rewrite规则与实例2025-02-22 00:15:39
-
MySql中身份证字段的简单脱敏介绍2025-02-22 00:15:36