published: true
tags:
- Linux programming
author: persuez
**注:**以下实验环境为Ubuntu 18.0404LTS, gcc 7.3.0
GCC
- 编译单个源文件main.c
- 生成目标文件:
gcc -c main.c
- 生成可执行文件:
gcc -o main main.c
,然后通过./main
执行
- 链接多个目标文件并生成可执行文件:
gcc -o test main.o test.o
- 链接库文件(以lib开头命名): 在默认路径查找-l(/lib和/usr/lib),链接libpam.a,
gcc -o main main.o test.o -lpam
;指定链接目录-L,gcc -o main main.o test.o -L/usr/local/lib/pam -lpam
- 在命令行中定义宏进行调试,
gcc -D NDEBUG main.c
,并且此时NDEBUG的值为1具体见gcc文档 - 优化:大部分用二级优化,
gcc -c -O2 main.c
Makefile 初览
Makefile所做的工作就如在IDE上自动化build项目一样,但是Makefile需要我们自己编写,然后用make
工具帮我们自动化执行。要做到自动化,需要以下几点:
- 目标(targets):你要告诉
make
要生成的目标文件是什么 - 规则(rules):你需要告诉
make
如何去生成目标文件 - 依赖(dependencies):因为之后可能会修改某个源文件,但可能这个被修改的文件会影响到其他文件,这个被修改的文件就是受影响文件的依赖。我们通过
make
文件定义了依赖之后,当依赖文件被修改之后,make
只会重新编译那些受依赖影响的文件,而不会再浪费时间去重新编译不受影响的文件。注意:依赖是传递的,就是说A被B依赖,B被C依赖,那A被修改了,B就得重新生成,然后C肯定也得重新生成。
以下给出我们的例子(求整数的倒数):
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include "reciprocal.hpp"
int main(int argc, char **argv)
{
int i;
i = atoi(argv[1]);
printf("The reciprocal of %d is %g\n", i, reciprocal(i));
return 0;
}
// reciprocal.cpp
#include <cassert>
#include "reciprocal.hpp"
double reciprocal(int i)
{
// i should be non-zero
assert(i != 0);
return 1.0 / i;
}
// reciprocal.hpp
#ifdef __cplusplus
extern "C" {
#endif
extern double reciprocal(int i);
#ifdef __cplusplus
}
#endif
我们要生成可执行文件reciprocal
,因此需要目标文件reciprocal.o
和main.o
。一般地,在Makefile中我们都会写clean
target,以便清除所有make
生成的文件(用make clean
命令)。所以,最终的Makefile如下(以下的$(CFLAGS)先不解释:
reciprocal: main.o reciprocal.o
g++ $(CFLAGS) -o reciprocal main.o reciprocal.o
main.o: main.c reciprocal.hpp
gcc $(CFLAGS) -c main.c
reciprocal.o: reciprocal.cpp reciprocal.hpp
g++ $(CFLAGS) -c reciprocal.cpp
clean:
rm -f *.o reciprocal
从上面的格式中可以明显看出:targets写在左边,如main.o,然后跟着冒号,接着是一些依赖,然后规则写在下一行(先忽略$(CFLAGS))。需要注意的是: 规则一行必须以一个Tab字符开头,不然make将会报错。运行如下:
然后我们稍微修改以下main.c
,运行结果如下:
可以看到,重新make,只有重新编译了main.c和reciprocal(因为依赖于main.o)。
接着运行make clean
:
现在我们说一下 $(CFLAGS),这是一个 make 系统变量,其中 CFLAGS 可以替换成其他变量名,如 a,b,c 等。CFLAGS
指定头文件的搜索路径,如果头文件不在 gcc 的默认搜索路径下,则需要用此参数指定,如 CFLAGS=-I/usr/include -I/path/include
,gcc 的默认搜索路径可以用 gcc -v -x c -E /dev/null
查看。
Makefile 编写编写规则
条件语句
例子
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
语法
conditional-directive
text-if-true
endif
或者
conditional-directive
text-if-true
else
text-if-false
endif
或者
conditional-directive-one
text-if-one-is-true
else conditional-directive-two
text-if-two-is-true
else
text-if-one-and-two-are-false
endif
在 conditional-directive
中,有以下五种的格式:
- ifeq (arg1, arg2)
- ifeq ‘arg1’ ‘arg2’
- ifeq “arg1” “arg2”
- ifeq “arg1” ‘arg2’
- ifeq ‘arg1’ “arg2”
此外,有一种特殊的比较,和空值比较:
ifeq ($(strip $(foo)),)
text-if-empty
endif
函数
notdir
:剥离文件的绝对路径,只保留文件名。$(dir <names...>)
:
名称:取目录函数 —— dir。
功能:从文件名序列 中取出目录部分。目录部分是指最后一个反斜杠("/")之前的部分。如果没有反斜杠,那么返回"./"。
返回:返回文件名序列 的目录部分。
示例:$(dir src/foo.c hacks)
返回值是 “src/ ./”。
更多好玩的内容请关注我的公众号~会介绍一些 linux 上的效率工具,C 语言, linux 咨询以及 linux 内核源码学习。