一、Makefile
1.1、简单的Makefile
下面是一个最简单的Makefile,其中第二行第四行,前面并不是空格,而是tab。
all:
@echo "hello all"
test:
@echo "hello test"
当创建好之后,进行make,当不加任何参数,默认就是第一个:
>>make
>>hello all
在make后可以加参数,指定输出哪一个名称:
>>make all
>>hello all
>>make test
>>hello test
1.2、Make file三要素
Makefile的三要素包含:依赖,目标,命令。我们使用下面这个Makefile来进行讲解。
依赖:编译所需要的文件。
目标:通过编译后生成的目标文件。
命令:对文件进行编译的命令。
其中的.PHONY是伪目标:它不代表一个真正的文件名,在执行make时可以指定这个目标来执行所在规则定义的命令,有时也可以将一个伪目标称为标签。
如果我们在Makefile中有clean的目标:rm .在这里面执行删除的命令,但是当前目录中含有clean的文件名,我们此时make clean,就不会执行咱们的命令了。因此加上伪命令后,就一定会执行我们自己的命令。这样的操作不仅提高makefile的执行速率,而且还不会寻找clean的隐含规则。
当执行这个makefile时使用make,由于simple的后面含有main和foo这两个依赖,那么会先执行依赖的目标,那么执行make后会先执行main和foo的命令,下面是控制台输出的命令:
gcc -o main.o -c main.c
gcc -o foo.o -c foo.c
gcc -o simple main.o foo.o
1.3、变量
我们可以通过设置变量将一些关键字变成较短或自己喜欢的名称。如下图:
.PHONY : clean
CC = gcc
RM = rm
EXE = simple
OBJS =main.o foo.o
$(EXE):$(OBJS)
$(CC) -o $(EXE) $(OBJS)
main.o:main.c
$(CC) -o main.o -c main.c
foo.o:foo.c
$(CC) -o foo.o -c foo.c
clean:
$(RM) $(EXE) $(OBJS)
我们将这个使用变量的图和上面那个没有使用变量的图进行比较会发现,也就是使用了一些变量来替换了这些,写的更加高级一点且更为方便。
CC:保存编译器名
RM:用于指示删除文件的命令
EXE:存放可执行文件名
OBJS:放置所有的目标文件名
1.4:自动变量与编译
自动变量:我们通过使用自动变量来方便的表示一些目标:
$@:用于表示一个规则中的目标。当我们的一个规则中有多个目标时,$@所指的是其中任何命令被运行的目标。
$^:表示的是规则中所有的先择条件。
$<:表示的是规则中的第一个先决条件。
.PHONY: all
all: first second third
@echo "\$$@ = $@"
@echo "$$^ = $^"
@echo "$$< = $<"
first:
@echo "1 first"
second:
@echo "2 second"
third:
@echo "3 third"
# 下面经过打印后输出:
# 1 first
# 2 second
# 3 third
# $@ = all
# $^ = first second third
# $< = first
编译:当我们目录下所需要使用的文件很多的时候,那么就需要自动获取这些文件,不然是一个体力活。我们使用wildcard和patsubst。
wildcard:通配符函数,通过它可以得到我们所需要的文件。$(wildcard pattern)
patsubst:字符串替换函数。$(patsubst pattern ,replacement ,text)
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
#SRCS = main.c foo.c
SRCS = $(wildcard *.c) #全部的。c文件
# 把.c换成对应的.o
#OBJS = foo.o foo2.o main.o
OBJS = $(patsubst %.c,%.o,$(SRCS)) #通过这个变成全部的。o文件
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
src: # 测试make src显示相应的xx.c
@echo $(SRCS)
objs:# 测试make objs显示相应的xx.o
@echo $(OBJS)
1.5、依赖第三方库
可以指定存放头文件和库文件的地址,以及链接库。
# 定义HEADER_PATH为当前工程中的头文件路径
HEADER_PATH = -I ./include/
# 定义LIB_PATH为当前工程中的库文件路径
LIB_PATH = -L ./lib/
# 制定LIBS链接库的名称
LIBS=-lpthread
# lib中的库文件名称为libpthread.so
二、cmake
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档 ,然后再依一般的建构方式使用。
2.1、编译方式
小伙伴肯定从github上拿过一些源文件吧,我们拿到之后,有些并不能直接使用,而是需要先cmake。这时候需要我们先找到cmake的文件:CMakeLists.txt。
为了生成的文件不影响我们已有的文件,我们一般会创建build目录,然后进入,并执行cmake ../ 将文件生成在build中,此时并不会执行完毕,因为cmake只是生成完整的makefile文件,所以还需要我们自己执行make命令,如果需要添加到系统目录中,需要make install 。
2.2、单个文件目录实现
# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程,他不是执行文件名
PROJECT(Darren)
# 手动加入文件 ${变量名}} ,比如${SRC_LIST}
SET(SRC_LIST main.c)
set(SRC_LIST2 main2.c)
# MESSAGE和echo类似
MESSAGE(STATUS "PROJECT_BINARY_DIR DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "PROJECT_SOURCE_DIR DIR " ${PROJECT_SOURCE_DIR})
# 通过生成后就是下面的
#-- PROJECT_BINARY_DIR DIR /home/tc/1.4.1-makefile-cmake/src-cmake/2.1-1
#-- PROJECT_SOURCE_DIR DIR /home/tc/1.4.1-makefile-cmake/src-cmake/2.1-1
# 生产执行文件名0voice 0voice2
ADD_EXECUTABLE(0voice ${SRC_LIST})
ADD_EXECUTABLE(0voice2 ${SRC_LIST2})
如果我们将一些不同的文件进行分发到不同的文件夹中去,那我们就需要通过添加子目录,让系统来进行搜索了。
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
PROJECT(0VOICE)
MESSAGE(STATUS "0voice top PROJECT_BINARY_DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "0voice top PROJECT_SOURCE_DIR " ${PROJECT_SOURCE_DIR})
# CMAKE_CURRENT_SOURCE_DIR它的CMakeLists.txt所在的当前源目
MESSAGE(STATUS "0voice top CMAKE_CURRENT_SOURCE_DIR " ${CMAKE_CURRENT_SOURCE_DIR})
# 添加子目录
ADD_SUBDIRECTORY(src)
#INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/0voice)
# 安装doc到 share/doc/cmake/0voice目录
# 默认/usr/local/
#指定自定义目录,比如 cmake -DCMAKE_INSTALL_PREFIX=/tmp/usr ..
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/0voice)
2.3、多个目录实现
下面是通过手动添加子目录,这是当目录不多的时候,通过添加子目录,系统会搜索这个目录,但是也需要我们手动链接目录。
# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程
PROJECT(0VOICE)
# 手动加入文件
SET(SRC_LIST main.c)
MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})
# 添加头文件路径
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir1")
# 相对路径的方式
INCLUDE_DIRECTORIES(dir1)
MESSAGE(STATUS "CMAKE_CURRENT_SOURCE_DIR -> " ${CMAKE_CURRENT_SOURCE_DIR})
# 添加 dir1 子目录
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir1")
# 添加头文件路径
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/dir2")
# 添加 dir2 子目录
ADD_SUBDIRECTORY("${CMAKE_CURRENT_SOURCE_DIR}/dir2")
ADD_EXECUTABLE(darren ${SRC_LIST} )
TARGET_LINK_LIBRARIES(darren dir1 dir2)
# 将执行文件安装到bin目录
INSTALL(TARGETS darren RUNTIME DESTINATION bin)
当子目录中文件很多的时候,我们就要通过系统自己去搜索目录中的文件,自动将所有源文件和头文件全部添加。通过使用aux_source_directory来搜索所有源文件。
# 单个目录实现
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 工程
# PROJECT(0VOICE)
# 手动加入文件
SET(SRC_LIST main.c)
MESSAGE(STATUS "THIS IS BINARY DIR " ${PROJECT_BINARY_DIR})
MESSAGE(STATUS "THIS IS SOURCE DIR " ${PROJECT_SOURCE_DIR})
# 设置子目录
set(SUB_DIR_LIST "${CMAKE_CURRENT_SOURCE_DIR}/dir1" "${CMAKE_CURRENT_SOURCE_DIR}/dir2")
foreach(SUB_DIR ${SUB_DIR_LIST})
# 自动添加头文件路径
INCLUDE_DIRECTORIES(${SUB_DIR})
#遍历源文件
aux_source_directory(${SUB_DIR} SRC_LIST)
MESSAGE(STATUS "SUB_DIR-> " ${SUB_DIR})
MESSAGE(STATUS "SRC_LIST-> " ${SRC_LIST})
endforeach()
# 手动 添加头文件路径
# INCLUDE_DIRECTORIES("dir1")
# INCLUDE_DIRECTORIES("dir2")
ADD_EXECUTABLE(darren ${SRC_LIST} )
# 将执行文件安装到bin目录
INSTALL(TARGETS darren RUNTIME DESTINATION bin)
上面两种编译方式有什么区别呢?第一种方式是将子目录编译成库文件,然后再给上一级进行链接。而第二种是将子目录的源文件和上一级源文件一同编译。
2.4、生成库
生成静态库和动态库,生成到指定目录。
#设置lib库目录
# RELEASE_DIR 自定义的变量
SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)
# debug和release版本目录不一样
#设置生成的so动态库最后输出的路径
# SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType})
# LIBRARY_OUTPUT_PATH
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/${BuildType})
MESSAGE(STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH})
ADD_COMPILE_OPTIONS(-fPIC)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
# 默认生成静态库链接库Dir1
#ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})
# SHARED生成动态库
ADD_LIBRARY (Dir1 SHARED ${DIR_LIB_SRCS})
2.5、调用库
# 引用动态库
ADD_EXECUTABLE(darren ${SRC_LIST})
TARGET_LINK_LIBRARIES(darren Dir1) # 正确
# TARGET_LINK_LIBRARIES(darren libDir1) # 错误
TARGET_LINK_LIBRARIES(darren libDir1.so) # 正确
# 强制使用静态库 完整的库文件名libDir1.a
#TARGET_LINK_LIBRARIES(darren lDir1)
TARGET_LINK_LIBRARIES(darren libDir1.a)
感谢大家的观看!0voice · GitHub