文章目录
- 一、makefile
- 二、cmake
- 1)cross-platform development
- 2)语法特性介绍
- 3)Cmake重要指令
- (1)cmake_minimum_required
- (2)project 定义工程名称,并指定工程支持的语句
- (3)set 显示的定义变量
- (4)include_directories 向工程添加多个特定的头文件搜索路径
- (5)link_directories-向工程添加多个特定的库的文件搜索路径(只是提供路径)
- (6)add_library-生成库文件
- (7)add_compile_options-添加编译参数
- (8)add_executable-生成可执行文件
- (9)add_executable-生成可执行文件
- (10)target_link_libraries-为target添加需要链接的共享库
- (11)add_subdirectory-向当前工程添加存在源文件的子目录,并可以指定中间二进制和目标二进制存放的位置(添加子目录)
- (12)aux_source_directory-发现一个目录下所有的源代码文件并将列表存储再一个变量中,这个指令临时被用来自动构建源文件列表(用来做库的命令,等等添加当前目录的.c和.h文件)
- (13)MESSAGE 往屏幕打印信息
- (14)INSTALL 安装二进制、动态库、链接库或文件目录等等到对应目录
- (15)SET_TARGET_PROPERTIES生成名字相同的静态库和动态库
- (16)SET(LIBRARY_OUTPUT_PATH <路径>)指定⼀个新的位置给生成的库文件
- (17)SET_TARGET_PROPERTIES设置动态库版本号
- 4)Cmake常用变量
- (1)CMAKE_C_FLAGS gcc编译选项
- (2)CMAKE_CXX_FLAGS g++编译选项
- (3)CMAKE_BUILD_TYPE 编译类型(Debug , Release)
- (4)CMAKE_BINARY_DIR 和 CMAKE_SOURCE_DIR
- (5)CMKAE_C_COMPILER: 指定C编译器
- (6)CMAKE_CXX_COMPILER:指定C++编译器
- (7)EXECUTABE_OUTPUT_PATH:可执行文件输出的存放路径
- (8)LIBRARY_OUTPUT_PATH:库文件出输出的存放路径
- (9)CMAKE_BUILD_TYPE设置cmake编译是debug还是release
- 5)CMake编译工程
- 6)【实战】CMake代码实践
- 三、其他编译稀奇古怪的问题
一、makefile
0.make零散知识补充
- 原因:
因为以前是只用过makefile所以,复习下,顺便总结cmake怎么写
- 1)makefile三个要素:依赖、目标、命令
- 2)对于make ⼯具,⼀个⽂件是否改动不是看⽂件⼤⼩,⽽是其时间戳
- 3)在现实中也难免存在所定义的⽬标与所存在的⽂件是同名的,可以用.PHONY
1.第一个makefile
.PHONY: test #用 .PHONY),这就告诉 make 不用检查它们 是否存在于磁碟
test: #echo 前⾯必须只有 TAB, echo功能是打印字符串到终端上
@echo "hello test"
all: test
@echo "hello all"
#运⾏“make all”命令,这告诉 make ⼯具,我要⽣成⽬标 all
#第一个目标是默认生成的
2.第二个makefile
//main.c
extern void foo();
int main()
{
foo();
return 0;
}
//foo.c
#include <stdio.h>
void foo()
{
printf ("This is foo ()!\n");
}
//makefile
#第一个目标是默认生成的
.PHONY: clean
all: main.o foo.o #生成simple执行文件,依赖两个文件
gcc -o simple main.o foo.o
main.o:
gcc -o main.o -c main.c
foo.o:
gcc -o foo.o -c foo.c
clean: #删除.o文件和执行文件
rm simple main.o foo.o
- 依赖树
3.第三个makefile
.PHONY: all
all: first second third
@echo "\$$@ = $@"
@echo "$$^ = $^"
@echo "$$< = $<"
first: #生成一个bin目录
mkdir bin
second third:
# $@用于标识一个规则中的目标,当我们的一个规则中有多个目标时,$@指的是其中任何造成命令被运行的目标
# $^表示的是规则中的所有先决条件(依赖)
# <表示的是规则中的第一个先决条件
# 想采⽤ echo 输出‘$’,则必需⽤两个连着的‘$’
# $@对于 Shell 也有特殊的意思,我们需要在“$$@”之前再加⼀个脱字符‘\’
4.第四个makefile
.PHONY: clean
CC = gcc #CC⽤于保存编译器名
RM = rm #RM⽤于指示删除⽂件的命令
EXE = simple #EXE⽤于存放可执⾏⽂件名
# SRCS = main.c foo.c
SRC$ = $(wildcard *.c) #wildcard搜索所有.c文件
# patsubst把.c换成对应的.o
# OBJS = foo.o foo2.o main.o
OBJS = $(patsubst %.c,%.o,$(SRCS)) # OBJS⽤于放置所有的⽬标⽂件名(.c、.o文件)
$(EXE): $(OBJS) #生成EXE需要OBJS,OBJS需要SRC
$(CC) -o $@ $^
%.o: %.c #.c生成点.o,$@表示.o文件,$^表示.c文件
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
# $@⽤于表示⼀个规则中的⽬标。当我们的⼀个规则中有多个⽬标时,$@所指的是其中任何造成命令被运⾏的⽬标
# $^则表示的是规则中的所有先择条件
# $<表示的是规则中的第⼀个先决条件
5.第五个makefile
.PHONY: all clean
MKDIR = mkdir
RM = rm
RMFLAGS = -fr
CC = gcc
DIR_OBJS = objs
DIR_EXES = exes
DIRS = $(DIR_OBJS) $(DIR_EXES)
EXE = complicated
#让生成的执行文件放到exes目录
EXE := $(addprefix $(DIR_EXES)/,$(EXE))
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
#让生成的.o放在objs目录
OBJS := $(addprefix $(DIR_OBJS)/,$(OBJS))
all: $(DIRS) $(EXE)
$(DIRS):
$(MKDIR) $@
$(EXE)
$(CC) -o $@ $^
$(DIR_OBJS)/%.o: %.c foo.h
$(CC) -o $@ -c $<
clean:
$(RM) $(RMFLAGS) $(DIRS)
二、cmake
1)cross-platform development
跨平台不用cmake(需要做四项修改)
使用Cmake之后:(只需要修改cmakelist.txt)
2)语法特性介绍
- 基本语法格式
- 指令是大小写无关的,参数和变量大小是大小写相关的
- 变量使用${}方式取值,但是再IF控制语句中是直接使用变量名
3)Cmake重要指令
(1)cmake_minimum_required
(2)project 定义工程名称,并指定工程支持的语句
这里可选项【。。。】一般都不用
(3)set 显示的定义变量
(4)include_directories 向工程添加多个特定的头文件搜索路径
(5)link_directories-向工程添加多个特定的库的文件搜索路径(只是提供路径)
(6)add_library-生成库文件
前面定义的SRC为两个.c文件(默认生成静态库,生成动态库需要加SHARED)
(7)add_compile_options-添加编译参数
(8)add_executable-生成可执行文件
(9)add_executable-生成可执行文件
(10)target_link_libraries-为target添加需要链接的共享库
hello就是库的名字,main是可执行文件,这里是给可执行文件链接库
(11)add_subdirectory-向当前工程添加存在源文件的子目录,并可以指定中间二进制和目标二进制存放的位置(添加子目录)
(12)aux_source_directory-发现一个目录下所有的源代码文件并将列表存储再一个变量中,这个指令临时被用来自动构建源文件列表(用来做库的命令,等等添加当前目录的.c和.h文件)
把当前目录下的.c和.cpp文件都放到SRC这个目录中
()
(13)MESSAGE 往屏幕打印信息
(14)INSTALL 安装二进制、动态库、链接库或文件目录等等到对应目录
INSTALL指令⽤于定义安装规则,安装的内容可以包括⽬标⼆进制、动态库、静态库以及⽂件、⽬录、脚本等。INSTALL指令包含了各种安装类型,我们需要⼀个个分开解释:
安装可执行的目标文件
普通文件
目录
- 例子
顶级目录
子目录
- 目录结构
(15)SET_TARGET_PROPERTIES生成名字相同的静态库和动态库
- 但还是有潜在问题
(16)SET(LIBRARY_OUTPUT_PATH <路径>)指定⼀个新的位置给生成的库文件
下⾯我们⽤这个指令再来添加静态库:
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
(17)SET_TARGET_PROPERTIES设置动态库版本号
4)Cmake常用变量
(1)CMAKE_C_FLAGS gcc编译选项
c语言的编译选项
(2)CMAKE_CXX_FLAGS g++编译选项
#在g++编译选项后追加-std=c++11
set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 " )
(3)CMAKE_BUILD_TYPE 编译类型(Debug , Release)
#设置编译类型为debug
set(CMAKE_BUILD_TYPE Debug)
#设置编译为release
set(CMAKE_BUILD_TYPE Release)
(4)CMAKE_BINARY_DIR 和 CMAKE_SOURCE_DIR
(5)CMKAE_C_COMPILER: 指定C编译器
(6)CMAKE_CXX_COMPILER:指定C++编译器
(7)EXECUTABE_OUTPUT_PATH:可执行文件输出的存放路径
(8)LIBRARY_OUTPUT_PATH:库文件出输出的存放路径
(9)CMAKE_BUILD_TYPE设置cmake编译是debug还是release
编译指令
5)CMake编译工程
CMake目录结构:项目主目录存在一个CMakeLists.txt文件
(1)两种方式设置编译规则:
1.包含源文件的子文件包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可
2.包含原文件的子文件未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt文件中
(2)linux平台使用CMake构建C/C++工程的流程如下
1.编写CMakeLists.txt
2.执行cmake path
生成makefile(PATH是顶层CMakeLists.txt文件所在的路径)
3.使用make进行编译
(3)两种构建方式
1)内部构建:不推荐使用
2)外部构建:推荐使用
6)【实战】CMake代码实践
(1)二级目录
(2)多个目录实现
1)子目录编译成库文件
这里的aux_source_directory相当于set
CMAKE_CURRENT_SOURCE_DIR表示当前源码目录,就是src里面的目录
- 完整版cmakelists.txt
2)对应子目录没有cmakelists.txt(第二种方法3.2.1,不需要链接库文件)
(3)生成库文件(详细版)
补充:
PROJECT_SOURCE_DIR 跟着最近工程的目录
1)静态库和动态库生成
把路径拼起来
2)调用库去install
install后
(4)链接库文件
1)链接静态库
2)链接动态库
(5)调用库
1)调用动态库
2)调用静态库
(6)编译指定debug还是release版本
三、其他编译稀奇古怪的问题
1)gcc编译为什么要加-g选项
加上-g 选项,会保留代码的文字信息,便于调试下面两幅图是有无 -g 选项调试的区别
(我是用的是cgdb,比gdb稍微好用一点,文章结束附带cgdb下载教程)
先来一段简单的代码:
如果使用 gcc test.c, 再使用cgdb调试时的效果是这样的:
如果使用 gcc -g test.c, 再使用cgdb调试时的效果是这样的:
2)linux找不到动态链接库 .so文件的解决方法
3)/usr/bin/ld: cannot find -lc错误原因及解决方法
- 问题
在运行asapp书中第7章链接例子时,在使用gcc -static -o prog main.o ./libvector.a 命令进行链接时出现以下错误:
/usr/bin/ld: 找不到 -lc
collect2: 错误:ld 返回 1
-
原因
在新版本的linux 系统下安装 glibc-devel、glibc和gcc-c++时,都不会安装libc.a. 只安装libc.so. 所以当使用-static时,libc.a不能使用。只能报找不到libc了。 -
解决方法
安装 glibc-static
sudo yum install glibc-static