Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Automatic generation of dependencies (10)

2025-01-20 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/03 Report--

In our previous makefile study, the target file (.o) depended only on the source file (.c). So if there is also a header file in the source file, how does the compiler compile the source file and header file? Let's take a look at the defects caused by the compilation behavior: 1, the preprocessor inserts the code in the header file directly into the source file; 2, the compiler generates the target file only through the preprocessed source file; 3, the rule depends on the source file. The command may not be executed.

Let's see if there is anything wrong with the makefile below.

Makefile source code

OBJS: = func.o main.ohello.out: $(OBJS) @ gcc-o $@ $^ @ echo "Target File = > $@" $(OBJS):% .o:% .c @ gcc-o $@-c $^

Func.h source code

# ifndef _ FUNC_H_#define _ FUNC_H_#define HELLO "Hello D.T." void foo (); # endif

Func.c source code

# include # include "func.h" void foo () {printf ("void foo ():% s\ n", HELLO);}

Main.c source code

# include # include "func.h" int main () {foo (); return 0;}

Let's take a look at the compilation results.

We see that the string printing has been implemented correctly, so what if we want to change the string to Software in the func.h source file? Try to see if you can modify it successfully.

We see that when recompiling, it does not change because of the change of the header file, we do not have the relevant addition of the header file in makefile, and the content in the header file must be unchanged. Let's add the header file to the pattern rule, add func.h after% .c, and then take a look at the compilation result.

We saw that the compilation error occurred after the direct addition. Because the target after-c contains a header file, it cannot be compiled directly. We can compile only the first dependency% .c after% .o, so that we will not compile the func.h header file, and change the following $^ to $

< ,我们来看看效果 我们看到已经正确改过来了。经过上面的实验,我们看到:头文件作为依赖条件出现于每个目标对应的规则中,当头文件改动时,任何源文件都将会被重新编译(编译低效);当项目中头文件巨大时,makefile 将很难维护。那么我们的头脑中不禁会冒出这么个想法:通过命令对自动生成对头文件的依赖;将生成的依赖自动包含进 makefile 中;当头文件改动后,自动确认需要重新编译的文件。那么此时我们还需要知道一个命令,Linux 中的 sed 命令。sed 是一个流编辑器,用于流文本的修改(增、删、查、改);它可用于流文本中的字符串替换,其字符串替换方式为:sed 's:src:des:g',具体格式如下 sed 同样也支持正则表达式,在 sed 中可以用正则表达式匹配替换目标,并且可以使用匹配的目标生成替换结果。格式如下 下来我们以代码为例来看看 sed 命令是如何使用的 再来看看 gcc 关键编译选项,获取目标的完整依赖关系:gcc -M test.c;获取目标的部分依赖关系:gcc -MM test.c。makefile 如下 .PHONY : testtest : gcc -M main.c 编译结果如下 我们看到 -M 是获取了它的所有依赖关系,再来试试 -MM 呢 我们看到 -MM 后,它只依赖与 main.c func.h。我们可以拆分目标的依赖,即将目标的完整依赖差分为多个部分依赖。格式如下 我们来做个实验 .PHONY : a b ctest : a btest : b ctest : @echo "$^" 我们来打印看看目标 test 的依赖都有哪些,编译结果如下 那么我们思考下:如何将 sed 和 gcc -MM 用于 makefile,并自动生成依赖关系呢? 我们再来看看 makefile 中的 include 关键字,它类似于 C 语言中的 include,是将其它文件的内容原封不动的搬入当前文件。make 对 include 关键字的处理方式是在当前目录下搜索或指定搜索目标文件。如果搜索一成功,便将文件内容搬入当前 makefile 中;如果搜索失败,将会产生警告,以文件名作为目标查找并执行对应规则,当文件名对应的规则不存在时,最终产生错误。格式如下 下来还是以代码为例来进行说明 .PHONY : testinclude test.txtall : @echo "this is $@"test.txt : @echo "test.txt" @touch test.txt 我们在第 3 行包含 test.txt,可是当前目录下并没有 test.txt,然后触发 test.txt 的规则。因而会打印出 test.txt,然后再创建 test.txt,我们来看看编译结果 我们看到确实是创建了一个 test.txt 文件。那么在 makefile 中命令的执行是:1、规则中的每个命令默认是在一个新的进程中执行(Shell);2、可以通过接续符(;)将多个命令组合成一个命令;3、组合的命令依次在同一个进程中被执行;4、set -e 指定发生错误后立即退出执行。那么我们看看下面的代码会实现想要的功能吗? .POHONY : allall : mkdir test cd test mkdir subtest 我们来看看编译结果 我们看到在当前目录下创建了目录,但是 subtest 目录却不是在 test 目录下创建的,这是怎么回事呢?在第一条命令执行时创建了目录 test,此时这个进程已经关闭了;在第二条命令执行时,执行的是另一个进程,虽然它已经进入到目录 test 中,但是随着这个进程的关闭,又回到了当前目录;第三个进程是重新创建了目录 subtest。那么如何解决这个问题呢?直接利用 set -e 和 接续符来解决 .PHONY : testall : set -e; \ mkdir test; \ cd test; \ mkdir subtest 看看编译结果 那么我们之前思考问题的初步思路是:1、通过 gcc -MM 和 sed 得到 .dep 依赖文件(目标的部分依赖),技术点是规则中命令的连续执行;2、通过 include 指令包含所有的 .dep 依赖文件。技术点是当 .dep 依赖文件不存在时,使用规则自动生成。下面我们来看看解决方案是怎样的 ONY : all cleanMKDIR := mkdirRM := rm -frCC := gccSRCS := $(wildcard *.c)DEPS := $(SRCS:.c=.dep)include $(DEPS)all : @echo "all" %.dep : %.c @echo "Creating $@ ..." @set -e; \ $(CC) -MM -E $^ | sed 's,\(.*\)\.o[ :]*,objs/\1.o : ,g' >

$@ clean: $(RM) $(DEPS)

Let's take a look at the compilation results.

Let's analyze that before executing make all, it contains $(DEPS) through include, triggers pattern rules through $(DEPS), and then creates a folder. We see that there are two messages without folders in front of us. In fact, this message can be hidden. Let's add in front of include-just OK, let's see the effect.

We see that the first two messages have not been printed. So let's think about it again: how to organize the rules related to dependent files and the rules related to source code compilation, so as to form a fully functional makefile program? How do we organize .dep files to a specified directory in makefile? The initial idea is that when include finds that .dep files do not exist: 1, create deps files through rules and commands; 2, create all .dep files into the deps folder; 3, record the dependencies of target files in .dep files.

Let's come down and see what the preliminary code design looks like.

.PHONY: all cleanMKDIR: = mkdirRM: = rm-rfCC: = gccDIR_DEPS: = depsSRCS: = $(wildcard * .c) DEPS: = $(SRCS:.c=.dep) DEPS: = $(addprefix $(DIR_DEPS) /, $(DEPS)) include $(DEPS) all: @ echo "all" $(DIR_DEPS): $(MKDIR) $@ $(DIR_DEPS) /%. Dep: $(DIR_DEPS)% .c @ echo "Creating $@." @ set-e \ $(CC)-MM-E $(filter% .c, $^) | sed's,\ (.*\)\ .o [:] *, objs/\ 1.o:, g'> $@ clean: $(RM) $(DIR_DEPS)

Let's take a look at the compilation results and see if all .dep files are put into one deps file.

We see that it has achieved results. Let's take a closer look at make and there is a warning that main.dep has been modified, which means that main.dep has been recreated. So let's analyze why some .dep dependent files are created repeatedly. The time attribute of the deps folder changes depending on file creation, and make finds that the deps folder is newer than the corresponding target, which triggers the corresponding rules to re-parse and execute the command. So we know the reason, how to optimize this scheme at this time? We can use ifeq to dynamically determine the dependency of the .dep target. The specific makefile is as follows

.PHONY: all cleanMKDIR: = mkdirRM: = rm-frCC: = gccDIR_DEPS: = depsSRCS: = $(wildcard * .c) DEPS: = $(SRCS:.c=.dep) DEPS: = $(addprefix $(DIR_DEPS) /, $(DEPS)) all: @ echo "all" ifeq ("$(MAKECMDGOALS)", "all")-include $(DEPS) endififeq ("$(MAKECMDGOALS)" "")-include $(DEPS) endif$ (DIR_DEPS): $(MKDIR) $@ ifeq ("$(wildcard $(DIR_DEPS))", ") $(DIR_DEPS) /%. Dep: $(DIR_DEPS)% .celse $(DIR_DEPS) /% .dep:%. Cendif @ echo" Creating $@. "@ set-e \ $(CC)-MM-E $(filter% .c, $^) | sed's,\ (.*\)\ .o [:] *, objs/\ 1.o:, g'> $@ clean: $(RM) $(DIR_DEPS)

Let's compile again.

We see that it still reported such an error, which may be caused by the optimization of the compiler. The train of thought is correct. Let's take a look at some little-known secrets of include.

A. using the minus sign (-) turns off not only warnings issued by include, but also errors; make ignores these errors when they occur! Take the code as an example to analyze and illustrate.

.PHONY: allinclude test.txtall: @ echo "this is all" test: @ echo "creating $@..." @ echo "other:; @ echo" this is other "" > test.txt

Let's compile and see.

We saw that not only a warning was issued, but also a wrong report was made. Let's add it in front of include.-try it.

In this way, it didn't make a mistake, it just passed, and we thought makefile was right. This is the first dark operation. Come down and take a look at the second dark operation.

B. what happens after the include trigger rule creates the file? Take the code as an example to analyze and illustrate.

.PHONY: allinclude test.txtall: @ echo "this is all" test.txt: @ echo "creating $@..." @ echo "other:; @ echo" this is other "" > test.txt

Look at the compilation results.

When we do the direct make, we find that the output this is other is not the this is all we expected. Why is that? Because in include, the test.txt is rolled out directly here, and the rule is triggered at this time. Makefile becomes something like this.

.PHONY: allother: @ echo "creating $@..." @ echo "this is other" all: @ echo "this is all"

When we make directly, it executes the first target by default, so it outputs this is other and this is all only when we make all. This is the second dark operation of include. Let's move on to the third one.

C. What happens after that if the file included in include exists? Take the code as an example to analyze and illustrate.

.PHONY: all-include test.txtall: @ echo "this is all" test.txt: b.txt @ echo "this is $@"

Create a b.txt file in the current directory and see the compilation result

We see that the corresponding rules of test.txt are also implemented. See what the following makefile will output

.PHONY: all-include test.txtall: @ echo "$@: $^" test.txt: b.txt @ echo "creating $@..." @ echo "all: c.txt" > test.txt

Look at the results.

Shouldn't we be surprised to see that its final output all depends on c.txt? We obviously have no dependence behind all. Let's take a look at the generated test.txt file, which contains all: c.txt, so the output is unexpected. Then we have the following summaries about include: 1. When the target file does not exist, find the rule with the file name and execute it; 2. When the target file does not exist and the found rules create the object file, and include the successfully created object file in the current makefile 3. When the object file exists, the target file is included in the current makefile, and the corresponding rules are found by the target file name. In the case of YES, the dependencies of the rules are compared to determine whether or not to execute the command of the rule. In the case of NO, NULL (no operation). 4. When the target file exists and the rule corresponding to the target name is executed, the command in the rule updates the target file, and make recontains the target file, replacing the previous content. If the target file is not updated, it is NULL (no operation).

After the exploration of so many knowledge points, we already have the ability to realize the previous ideas. The specific format you want to implement is as follows

Let's write the relevant makefile based on this.

Func.h source code

# ifndef FUNC_H#define FUNC_H#define HELLO "hello Makefile" # endif

Func.c source code

# include # include "func.h" void foo () {printf ("void foo ():% s\ n", HELLO);}

Main.c source code

# include # include "func.h" int main () {foo (); return 0;}

Makefile source code

.PHONY: all cleanMKDIR: = mkdirRM: = rm-rfCC: = gccDIR_DEPS: = depsDIR_OBJS: = objsDIR_EXES: = exesDIRS: = $(DIR_DEPS) $(DIR_EXES) $(DIR_OBJS) EXE: = app.outEXE: = $(addprefix $(DIR_EXES) /, $(EXE)) SRCS: = $(wildcard * .c) OBJS: = $(SRCS:.c=.o) OBJS: = $(addprefix $(DIR_OBJS) / $(OBJS)) DEPS: = $(SRCS:.c=.dep) DEPS: = $(addprefix $(DIR_DEPS) /, $(DEPS)) all: $(DIR_OBJS) $(DIR_EXES) $(EXE) ifeq ("$(MAKECMDGOALS)", "all")-include $(DEPS) endififeq ("$(MAKECMDGOALS)", ")-include $(DEPS) endif$ (EXE): $(OBJS) $(CC)-o $@ $^ @ echo" Success! Target = > $@ "$(DIR_OBJS) /% .o:% .c $(CC)-o $@-c $^ $(DIRS): $(MKDIR) $@ ifeq (" $(wildcard $(DIR_DEPS)) ",") $(DIR_DEPS) /% .dep: $(DIR_DEPS)% .celse $(DIR_DEPS) /% .dep:%. Cendif @ echo "Creating $@..." @ set-e \ $(CC)-MM-E $(filter% .c, $^) | sed's,\ (.*\)\ .o [:] *, objs/\ 1.o $@:, g'> $@ clean: $(RM) $(DIRS)

The compilation results are as follows

We see that it has been generated automatically, and the final result is what we want, so if we change the string in func.h, see if the result will also change.

We see that an error was reported during compilation because the .c file can only be compiled, and the .h header file does not participate in the compilation, so we will use the predefined function filter. So we need to change it to $(CC)-o $@-c $(filter% .c, $^) at line 37 of makefile; let's see how it works.

We saw that it was successfully replaced. At this time, we have basically completed our previous idea, so in actual development, we must need to add header files from time to time. Let's include a header file define.h in func.h and define the string hello-makefile in the define.h file to see if the result will change.

We see that the string has not changed, so let's see if define.h is included in func.dep and main.dep.

It's not included, and it shouldn't be, because we include define.h in func.h, so we definitely include define.h in func.c and main.c. Let's analyze this. When the .dep file is generated, if the dependency between the header files is changed dynamically, then make may not be able to detect the change and make the wrong compilation decision. The solution is: 1, add the dependency file name as the target to the automatically generated dependency; 2, determine whether to execute the rule when loading the dependency file through include; 3, regenerate the dependency file when the rule is executed; 4, finally load the new dependency file. The solution is to add $@ after the sed command to see the compilation, and by the way we'll add rebuild.

We see that it has been implemented correctly. Let's see if the .dep file under the deps file contains define.h.

It does include define.h. Let's add new.h to see if it still works.

We see that new.h is also included. Through the study of the comprehensive example, the summary is as follows: 1. The dependency of the target can be split and written to different places in makefile; 2. The include keyword can trigger the execution of the corresponding rules; 3. If the execution of the rules leads to dependency updates, it may lead to the interpretation and execution of the corresponding rules again; 4. The dependent files also need to rely on the source files to make correct compilation decisions. 5. Automatically generating the dependency relationship between files can improve the portability of makefile.

Welcome to learn makefile, you can add me QQ:243343083.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report