GCC&Makefile&GDB

GCC

header 头文件

include 使用 “” or <> : precedence is different (mine? Or system’s.)
gcc 在编译时如何去寻找所需要的头文件 :

  1. 使用双引号包含的头文件会首先搜索当前工作目录
  2. 然后与<XXX> 一致, header file的搜寻会从 -I 开始
  3. 然后找gcc的环境变量 C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
  4. 再找内定目录
    /usr/include
    /usr/local/include(centos7中该目录下是空的)
    gcc的一系列自带目录
  • e.g. /usr/include/c++/x.x.x
  • 如果想引用位于标准位置之外的头文件,需要在调用编译器的时候加上-I标志,来显式的说明头文件所在文件夹,e.g: $ gcc -I/usr/openwin/include hello.c

lib 库文件

编译的时候gcc

  1. 搜索-L指定目录;
  2. 再找gcc的环境变量LIBRARY_PATH
  3. 再找内定目录 /lib:/usr/local/lib :/usr/lib
  • 这是当初compile gcc时写在程序内的
  • /usr/local/lib prior to /usr/lib

运行时动态库的搜索路径 :

  1. 编译目标代码时指定的动态库搜索路径;
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
  4. 默认的动态库搜索路径/lib, /usr/lib

Makefile

Recommended

弄懂以上两份材料, makefile的书写也就入门了,剩下的就得在实际工作中磨砺。

如果你没有时间阅读或者第一次学习感觉吃力,以下是个人总结和补充。

Autotools

  • Windows下的IDE都自动生成了Makefile. Linux下自动生成Makefile的工具有autotools、qmake 、Cmake 等.

  • @@在做一个大的项目的时候,不可能都靠人来写Makefile,这样太麻烦了!很多GNULinux的的软件都是用它生成Makefile的,包括我们非常熟悉的Linux内核源代码。

  • 对于开源或者大型项目,一般都使用工具自动生成Makefile。比如GNU开源软件,一般是使用Autotools,还有Cmake、Scons等。

  • 对于小项目,使用Makefile还是很方便的的,至少手写的Makefile可读性很好,维护成本也不大。

  • 学习Makefile还是很有必要的,至少会让你对软件构件系统、程序的编译和链接、库的生成和使用有一个直观的理解。特别是对从事底层开发、调试大有裨益。
    对于从事应用、业务逻辑软件开发,建议还是使用自动工具,时间精力允许,多学也是总有益处,艺不压身。

  • autotools给人感觉好像是到处都在使用,几乎可以代替make。但事实上非常多的大项目的makefile还是手写的(So,具体怎么选择还是取决于你的项目、能力以及及老板口味)。

  • autotools核心是解决移植性的问题,学习成本其实是很高的;对于初学者,用make来完成你自己小型项目的编译,学习成本要小的多;即使是公司中,很多内部使用的项也目都是使用手写makefile来管理。@

  • 听起来很乱,其实是因为这个问题本身就没有标准答案。我能告诉你的就是:如果你没得选,就不要瞎纠结;有的选,就多学点吧,毕竟业务层的技术变化这么快,基本功不行的话,终究要还的, 这也是科班与半路出家的差距,不要太狂,要正视不足。

具体使用时,Linux下,小工程可手动写Makefile; 大工程用automake来帮你生成Makefile;要想跨平台,就用cmake。如果GUI用了Qt,也可以用qmake+*.pro来管理工程,这也是跨平台的。当然,cmake中也有针对Qt的一些规则,并代替qmake帮你将qt相关的命令整理好了

how to write your makefile

首先你必须牢牢记住:
Makefile concerns the compiling rules for the whole project. 它关心的是文件之间的依赖关系。

“target” 依赖”prerequisites”, 其生成规则定义在command. If “Prerequisites” 中 any file(s) is(are) newer than “target”, “command” 所定义的命令就会执行. This is Makefile’s rule , its core.

  • 一个makefile文件包含五个要素:显式规则、隐晦规则、变量定义、文件指示、注释。
  • 显式规则需要自己写;
  • 隐晦规则是自动被支持的,这“隐晦”二字也是学习makefile的难点,如果你没能深入理解这类规则,很难看懂makefile里都是些甚。
  • makefile中的变量,准确地讲更像是宏macro(但是可以修改),是个字符串string,会被自动替换,自然也就不存在变量类型一说。
  • 可以使用#include<filename> / #if 如果你不是只学过python,应该不陌生吧。
  • 注释采用 “#” 单行注释,类似“//”。 其实这也是比较通用的注释方式,shell、python等脚本语言都采用此方式。
  • 一个makefile 的基本框架
Target  ……… : prerequisites ………
             Command   ………
.PHONY : clean
Clean :
    -rm  exe_name  $(objects) 
  • 目标文件 ObjectFile, or 执行文件, or label. 第一行的第一个目标is default.
  • prerequisites 是生成target 所需要的文件or目标.
  • Make 所需要执行的命令, any shell command(executed by standard hell/bin/shell)
  • .phony 伪目标.
  • “clean” is like a “label” in C. 当然,C语言也不提倡使用label。
  • Difference between “rm” and “-rm” : maybe some files error, do not care, continue!
  • make 的执行步骤
  • Save as ‘Makefile” or “makefile ”;
  • 在该目录下直接输入命令”make” ,you can get the executable file.
  • “make” only concern “dependency”, if error then aborted with ERRNO, it doesn’t care about the “commands”(even wrong)
  • use command “make clean” to execute “%directives%” specified in clean:
    The commands succeeding “clean” cannot execute automatically for there is no “dependency ” after colon. 也就是说你要用make clean之类的命令。

在这里插入图片描述

读取makefile的顺序 In order :GNUmakefile() > makefile(some platform may support this only) > Makefile(recommended).
You can also use other user defined name and use “make -f filename”

  • 所谓“更新newer” Including the case that “target” doesn’t exist,this process is similar to stack—nested–call.
  • Each commands line begin with a “tab”.

If succeeding commands continues after the preceding: write them in the same line, separated by semi-colon.
Use “make -I “ or “-command” to ignore the errors,instead of aborting.

变量

  1. definition and assignment
  2. use by ${var} or $(var)

var = value Vs var := value

“=” permits 用尚未初始化”的var to assign, which may cause 死循环.
“:=” only permit those initialized in advance.
“?=” if already defined , do nothing.
“+=” append.if undefined before”= ”; else inheritance

  • 变量替换
  1. $(var:a=b) or ${var:a=b} : replace substring “a” in string var by b.
  2. 静态模式 $(var:%.o=%.c)
  • 多行变量
    Define …… endef

  • 环境变量
    CFLAGS 统一的编译参数.
    在makefile中可以使用上一层级定义的变量 : 在命令行定义的变量能直接传递给下层; if defined in file, use “export” to declare.

  • 目标变量
    <target… > :

  • 模式变量
    <pattern …> : [override]
    如果你记不住, 用到再来查看吧.

隐含规则

“make” can derive files & commands automatically.

  • [Whatever.c/.h files] can be derived automatically , into [whatever .o file].
  • %头文件依赖自动生成%@如果头文件路径在cpp路径或者在搜索路径下,不用在Makefile下包含; 头文件如果头文件路径搜索不到,那么在g++里加上 -I …/…/ 选项。如g++ -I /usr/include -o test test.cpp%

用隐晦规则时 .o 文件会自动找对应的 .c文件.然后编译(%自动搞定header%)but if you changed header files—>wrong!!(source files not changed, makefile 以为啥都没变……) 所以得想办法。既能使用隐晦规则.又能包含了头文件.这就是最高宗旨.

  • gcc编译器有个功能 gcc -MM first.c 这样会输出first.o : first.c *.h
    if 在编译时加上-M选项, print the library files too, in GNU, it should be -MM.

-MMD选项 会把自动得到的依赖信息output into *.d file.

makefile 中 .d 文件的解释

GNU organization recommends compilers to generate a”name.d” file for each source file, containing the dependency automatically got .

以下代码相信你迟早会碰到:
在这里插入图片描述

这段代码的核心意思就是"把每个源文件都用gcc -MM输出从定向到一个对应的.d文件. "

  • @ —放在最前表示"不显示执行的命令", 通俗来讲就是"悄悄地进村,打枪地不要",这不光是shell中,在windows 批处理脚本也这么干.
  • set -e — 1. 当命令的返回值为非零状态时,则立即退出脚本的执行(防止一错再错)。 2. 作用范围只限于脚本执行的当前进行,不作用于其创建的子进程。
  • 用分号分隔命令,续行符续行.
  • rm是删除命令,"-f"指令要慎用!! “$@“表示target,这里就是指”%.d”,将依赖关系输出到.d 文件前需要将旧的删掉, 这也是编码的基本套路—在写文件之前先判断是否已经存在.
  • 第三行是重点. CC/CPPFLAGS是一个变量,这是优秀程序员的基本素质,因为编译器不止gcc一种, 所以用CC指代. 类似的思想存在于c/c++的macro/typedef , 当然, 使用const是推荐做法. $< 指代第一个依赖文件,即%.c; ">"是重定向输出符,使用linux环境的没有理由看不懂~. $@.$$ 中 中 $$代表随机编号, 得到类似shuaige.d.123455的文件.
  • sed的官方定义是"stream editor for filtering and transforming text".该sed命令就实现了把main.o :替换为main.o main.d :的目的
    关于sed, 这里多说几句

2 usage types : sed [options] ‘command’ file(s) & sed [options] -f scriptfile file(s)
s命令$ sed ‘s/test/mytest/g’ example-----在整行范围内把test替换为mytest。如果没有g标记,则只有每行第一个匹配的test被替换成mytest。
Here, use comma as delimiter.
($*).o[ :]替换为\1.o $@ :
$
,表示的是target的除去了suffix后的filename,也就是%.d: %.c当中的%部分。

伪目标

目标和伪目标也可以作为依赖条件.
伪目标的特点是"总是被执行", 也就是不用检查是否"newer".

条件表达式

makefile 其实很复杂,本身就是一门语言.
: ifeq / ifneq /ifdef / ifndef

 <conditional-directive>
<text-if-true>
Else
<text-if-false>
Endif

函数

既然makefile是一门程序设计语言,自然少不了函数了.
**$(functionname arguments) **

Seperated by whitespace; arguments separated by comma.
A few functions in make, but enough .

你只能使用系统自带的函数,不能自定义.
下面介绍几个常用函数.

  1. 字符串替换
    $(Subst <from>, <to>,<text>)
    Repalce <from> in string <text> with <to>.

run makefile

  1. 【make中命令行前面加上减号】
    忽略当前此行命令执行时候所遇到的错误。
    而如果不忽略,make在执行命令的时候,如果遇到error,会退出执行的,加上减号的目的,是即便此行命令执行中出错,比如删除一个不存在的文件等,那么也不要管,继续执行make。
    【make中命令行前面加上@】
    就是,在make执行时候,输出的信息中,不要显示此行命令。
    而正常情况下,make执行过程中,都是会显示其所执行的任何的命令的。如果你不想要显示某行的命令,那么就在其前面加上@符号即可.

一种推荐做法是用一个"宏", 比如加个变量echo=@, 然后在各行前面加上$(echo),如果需要显示,就把echo值设为空 . 就像c编程中 #define_DEBUG 或者#define STATIC static, 好的思想总是相通的嘛! 学习得要能够举一反三, 这也是为什么高手的知识面一般都很广.

debug

makefile还可以类似debug, 不过没有其它语言那么复杂.
makefile debug
–debug[=<options>]
-n : print the commands that would be executed , but do not execute them.

记不住也很正常, 如果连这些你都能记住,要么你明早有个面试,要么你明早要去招人.

嵌套 nested

在一些大的工程中,我们把不同模块或不同功能的源文件放在不同的目录, we can write a Makefile in every dir; then write a “总控Makefile”. 这种分级设计模式,太常见了,不论软件还是硬件都有它忙碌的身影. 机械/汽车等行业也把"模块化/层次化挂在嘴上".

$(MAKE) 宏变量 传递到下级.

-w argument: print current direcory when executing make.
make -p 可以查看所有预定义的变量的当前值。
System var”MAKELEVEL” : if make has a nested-executed action, MAKELEVEL stores the present 调用层数.

Make target

All: 这个伪目标是所有目标的目标, generally compile all targets.
Clean: remove all files generated by make.
Install: 安装已经编译好的程序,in fact, copy the target executable file into 指定目标。 在linux命令行界面下安装过软件的兄弟应该见过它.
Print : 列出改变过的源文件;
Tar : 打包
TAGS: update all targets , for 完整地重编译使用
Check& test : 测试makefile.

这里的check/test是指你自己定义的伪目标,不是debug指令.

杂记

一些常用的变量系统给出了预定义值

AR: default is “ar”;
AS: default is “as”;
CC: default is “cc”;
CXX: default is “g++”;
CPP: c 程序的预处理器(output: stdout) , default is “$(CC) -E”
RM: 删除文件命令 , default is “rm -f”.

部分常用的自动化变量

$@ : target file set.

$(@D) : 目录部分;
$(@F) : file part;

$% : 目标中的函数库文件
$< : 依赖目标中第一个目标名
$? : 所有比目标新的依赖目标的集合, seperated by whitespace.
$^ : all 依赖目标的集合, 去重;
$+ : all 依赖目标的集合, 不去重;
$* :

$* 这个变量表示目标模式中“%”及其之前的部分。如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$”就是空值。

更多有关make的细节,请参考man make.

GDB

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值