怎么查找网站后台,福州哪里会网站制作的,湖南省建设工程造价管理站网站,网站做桌面应用 iOS文章目录 一、背景二、Makefile编译过程三、变量四、变量赋值1、是最普通的等号2、“:” 表示直接赋值3、“?” 表示如果该变量没有被赋值#xff0c;4、和写代码是一样的#xff0c; 五、预定义变量六、函数**通配符** 七、伪目标 .PHONY八、其他常… 文章目录 一、背景二、Makefile编译过程三、变量四、变量赋值1、是最普通的等号2、“:” 表示直接赋值3、“?” 表示如果该变量没有被赋值4、和写代码是一样的 五、预定义变量六、函数**通配符** 七、伪目标 .PHONY八、其他常用功能代码清理clean 九、嵌套执行Makefile十、指定头文件路径十一、指定库文件路径十二、基本使用十三、推导过程十四、适度扩展语法 一、背景
• 会不会写makefile从一个侧面说明了一个⼈是否具备完成大型工程的能力 • 一个工程中的源文件不计数其按类型、功能、模块分别放在若干个目录中makefile定义了一系列的规则来指定哪些文件需要先编译哪些文件需要后编译哪些文件需要重新编译甚至于进行更复杂的功能操作 • makefile带来的好处就是⸺“自动化编译”一旦写好只需要一个make命令整个工程完全自动编译极大的提高了软件开发的效率。 • make是一个命令工具是一个解释makefile中指令的命令工具一般来说大多数的IDE都有这个命令比如Delphi的makeVisual C的nmakeLinux下GNU的make。可见makefile都成为了一种在工程方面的编译方法。 • make是一条命令makefile是一个文件两个搭配使用完成项目自动化构建。
二、Makefile编译过程 Makefile文件中的命令有一定规范一旦该文件编写好以后在Linux命令行中执行一条make命令即可自动编译整个工程。不同厂家的make可能会稍有不同并且语法上也有区别不过基本思想都差不多主要还是落在目标依赖上最广泛使用的是GNUmake。
2、语法规则
目标 ... : 依赖 ...命令1命令2. . .Makefile的核心规则类似于一位厨师做菜目标就是做好一道菜那么所谓的依赖就是各种食材各种厨具等等然后需要厨师好的技术方法类似于命令才能作出一道好菜。 同时这些依赖也有可能此时并不存在需要现场制作或者是由其他厨师做好那么这个依赖就成为了其他规则的目标该目标也会有他自己的依赖和命令。这样就形成了一层一层递归依赖组成了Makefile文件。 Makefile并不会关心命令是如何执行的仅仅只是会去执行所有定义的命令和我们平时直接输入命令行是一样的效果。
目标即要生成的文件。如果目标文件的更新时间晚于依赖文件更新时间则说明依赖文件没有改动目标文件不需要重新编译。否则会进行重新编译并更新目标文件。默认情况下Makefile的第一个目标为终极目标。依赖即目标文件由哪些文件生成。命令即通过执行命令由依赖文件生成目标文件。注意每条命令之前必须有一个tab(此文档编辑器默认是空格复制下来的代码需要把命令代码的缩进改为tab制表符)保持缩进这是语法要求。allMakefile文件默认只生成第一个目标文件即完成编译但是我们可以通过all 指定所需要生成的目标文件。 例如下面的例子:
all: target1 target2 target3
target1:
# 编译规则1
target2:
# 编译规则2
target3:
# 编译规则3all被设置为第一个目标并且target1、target2和target3被列为all的依赖。当你在命令行中运行make时make命令会寻找并执行all目标规则这将依次执行target1、target2和target3的编译规则。 因此通过在Makefile中设置all作为默认目标规则你可以简化构建过程只需运行make命令即可执行整个编译过程无需显式指定目标
三、变量
$符号表示取变量的值当变量名多于一个字符时使用( ) $符的其他用法
$^ 表示所有的依赖文件 $ 表示生成的目标文件 $ 代表第一个依赖文件
SRC $(wildcard *.c)
OBJ $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $ -o $%.o: %.cgcc -c $ -o $四、变量赋值
1、是最普通的等号
在Makefile中容易搞错赋值等号使用 “”进行赋值变量的值是整个Makefile中最后被指定的值。
VIR_A A
VIR_B $(VIR_A) B
VIR_A AA经过上面的赋值后最后VIR_B的值是AA B而不是A B在make时会把整个Makefile展开来决定变量的值类似于宏定义
2、“:” 表示直接赋值
赋予当前位置的值。VIR_A : A
VIR_B : $(VIR_A) B
VIR_A : AA最后BIR_B的值是A B即根据当前位置进行赋值。因此相当于“”“”才是真正意义上的直接赋值
3、“?” 表示如果该变量没有被赋值
赋值予等号后面的值。VIR ? new_value如果VIR在之前没有被赋值那么VIR的值就为new_value。
VIR : old_value
VIR ? new_value这种情况下VIR的值就是old_value
4、和写代码是一样的
表示将符号后面的值添加到前面的变量上五、预定义变量
CCc编译器的名称默认值为cc。 cpp c预编译器的名称默认值为$(CC) -E
CC gcc回显问题Makefile中的命令都会被打印出来。如果不想打印命令部分 可以使用去除回显
echo clean done!六、函数
通配符
SRC $(wildcard ./*.c)匹配目录下所有.c 文件并将其赋值给SRC变量。
OBJ $(patsubst %.c, %.o, $(SRC))这个函数有三个参数意思是取出SRC中的所有值然后将.c 替换为.o 最后赋值给OBJ变量。
示例如果目录下有很多个.c 源文件就不需要写很多条规则语句了而是可以像下面这样写
SRC $(wildcard *.c)
OBJ $(patsubst %.c, %.o, $(SRC))ALL: test.exe #生成执行文件test.exe: $(OBJ)gcc $(OBJ) -o test%.o: %.cgcc -c $ -o $这里先将所有.c 文件编译为 .o 文件这样后面更改某个 .c 文件时其他的 .c 文件将不在编译而只是编译有更改的 .c 文件可以大大提高大项目中的编译速度。
七、伪目标 .PHONY
伪目标只是一个标签clean是个伪目标没有依赖文件只有用make来调用时才会执行 当目录下有与make 命令 同名的文件时 执行make 命令就会出现错误。 解决办法就是使用伪目标
SRC $(wildcard *.c)
OBJ $(patsubst %.c, %.o, $(SRC))ALL: test.exetest.exe: $(OBJ)gcc $ -o $$(OBJ): $(SRC)gcc -c $ -o $clean:rm -rf $(OBJ) test.exe.PHONY: clean ALL通常也会把ALL设置成伪目标八、其他常用功能
代码清理clean
我们可以编译一条属于自己的clean语句来清理make命令所产生的所有文件列如
SRC $(wildcard *.c)
OBJ $(patsubst %.c, %.o, $(SRC))ALL: test.exetest.exe: $(OBJ)gcc $ -o $$(OBJ): $(SRC)gcc -c $ -o $clean:rm -rf $(OBJ) test.exe九、嵌套执行Makefile
在一些大工程中会把不同模块或不同功能的源文件放在不同的目录中我们可以在每个目录中都写一个该目录的Makefile这有利于让我们的Makefile变的更加简洁不至于把所有东西全部写在一个Makefile中。 列如在子目录subdir目录下有个Makefile文件来指明这个目录下文件的编译规则。外部总Makefile可以这样写
subsystem:cd subdir $(MAKE)
其等价于
subsystem:$(MAKE) -C subdir定义$(MAKE)宏变量的意思是也许我们的make需要一些参数所以定义成一个变量比较有利于维护。两个例子意思都是先进入subdir目录然后执行make命令 我们把这个Makefile叫做总控Makefile总控Makefile的变量可以传递到下级的Makefile中但是不会覆盖下层Makefile中所定义的变量除非指定了 -e参数。 如果传递变量到下级Makefile中那么可以使用这样的声明export 如果不想让某些变量传递到下级Makefile可以使用unexport
export variable value
等价于
variable value
export variable
等价于
export variable : value
等价于
variable : value
export variable
如果需要传递所有变量那么只要一个export就行了。后面什么也不用跟表示传递所有变量十、指定头文件路径
一般都是通过-I大写i来指定假设头文件在
/home/develop/include则可以通过-I指定
-I/home/develop/include将该目录添加到头文件搜索路径中 在Makefile中则可以这样写
CFLAGS-I/home/develop/include然后在编译的时候引用CFLAGS即可如下
yourapp:*.cgcc $(CFLAGS) -o yourapp十一、指定库文件路径
与上面指定头文件类似只不过使用的是-L来指定
LDFLAGS-L/usr/lib -L/path/to/your/lib告诉链接器要链接哪些库文件使用-l小写L如下
LIBS -lpthread -liconv十二、基本使用
实例代码
#include stdio.h
int main()
{ printf(hello Makefile!\n); return 0;
} Makefile文件
myproc:myproc.cgcc -o myproc myproc.c.PHONY:clean
clean:rm -f myproc 依赖关系 • 上面的文件myproc,它依赖myproc.c 依赖方法 • gcc -o myproc myproc.c ,就是与之对应的依赖关系 项目清理 • 工程是需要被清理的 • 像clean这种没有被第一个目标文件直接或间接关联那么它后面所定义的命令将不会被自动执行不过我们可以显示要make执行。即命令⸺“make clean”以此来清除所有的目标文件以便重编译。 • 但是一般我们这种clean的目标文件我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是总是被执行的。 • 可以将我们的 hello 目标文件声明成伪目标测试一下。 什么叫做总是被执行
$ stat XXXFile: ‘XXX’Size: 987 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 1321125 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ whb) Gid: ( 1000/ whb)
Access: 2024-10-25 17:05:30.430619002 0800
Modify: 2024-10-25 17:05:25.940595116 0800
Change: 2024-10-25 17:05:25.940595116 0800⽂件 内容 属性
Modify: 内容变更时间更新
Change属性变更时间更新
Access常指的是⽂件最近⼀次被访问的时间。在Linux的早期版本中每当⽂件被访问时其atime都会更新。但这种机制会导致⼤量的IO操作。具体更新原则不做过多解释。结论 .PHONY:让make忽略源文件和可执行目标文件的M时间对比
十三、推导过程
myproc:myproc.o gcc myproc.o -o myproc
myproc.o:myproc.sgcc -c myproc.s -o myproc.o
myproc.s:myproc.i gcc -S myproc.i -o myproc.s
myproc.i:myproc.c gcc -E myproc.c -o myproc.i .PHONY:clean
clean: rm -f *.i *.s *.o myproc编译
$ make
gcc -E myproc.c -o myproc.i
gcc -S myproc.i -o myproc.s
gcc -c myproc.s -o myproc.o
gcc myproc.o -o myprocmake是如何工作的,在默认的方式下也就是我们只输入make命令。那么
make会在当前目录下找名字叫“Makefile”或“makefile”的文件。如果找到它会找文件中的第一个目标文件target在上面的例子中他会找到 myproc 这个文件并把这个文件作为最终的目标文件。如果 myproc 文件不存在或是 myproc 所依赖的后面的 myproc.o 文件的文件修改时间要比 myproc 这个文件新可以用 touch 测试那么他就会执行后面所定义的命令来生成myproc 这个文件。如果 myproc 所依赖的 myproc.o 文件不存在那么 make 会在当前文件中找目标为myproc.o 文件的依赖性如果找到则再根据那一个规则生成 myproc.o 文件。这有点像一个堆栈的过程当然你的C文件和H文件是存在的啦于是 make 会生成 myproc.o 文件然后再用 myproc.o 文件声明 make 的终极任务也就是执行文件 hello 了。这就是整个make的依赖性make会一层又一层地去找文件的依赖关系直到最终编译出第一个目标文件。在找寻的过程中如果出现错误比如最后被依赖的文件找不到那么make就会直接退出并报错而对于所定义的命令的错误或是编译不成功make根本不理。make只管文件的依赖性即如果在我找了依赖关系之后冒号后面的文件还是不在那么对不起我就不工作啦。
十四、适度扩展语法
BINproc.exe # 定义变量
CCgcc
#SRC$(shell ls *.c) # 采⽤shell命令⾏⽅式获取当前所有.c⽂件名
SRC$(wildcard *.c) # 或者使⽤ wildcard 函数获取当前所有.c⽂件名
OBJ$(SRC:.c.o) # 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表
LFLAGS-o # 链接选项
FLAGS-c # 编译选项
RMrm -f # 引⼊命令$(BIN):$(OBJ)$(CC) $(LFLAGS) $ $^ # $:代表⽬标⽂件名。 $^: 代表依赖⽂件列表echo linking ... $^ to $
%.o:%.c # %.c 展开当前⽬录下所有的.c。 %.o: 同时展开同
名.o$(CC) $(FLAGS) $ # %: 对展开的依赖.c⽂件⼀个⼀个的交给gcc。echo compling ... $ to $ # 不回显命令.PHONY:clean
clean:$(RM) $(OBJ) $(BIN) # $(RM): 替换⽤变量内容替换它.PHONY:test
test:echo $(SRC)echo $(OBJ)