Makefile学习笔记(5)——Makefile显示命令,命令执行,命令出错,嵌套执行make,定义命令包

    xiaoxiao2024-10-28  79

    5.1Makefile显示命令

    通常,make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个 命令将不被 make 显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。

    以make clean为例

    当执行make cleanall时 

    会输出执行命令,当在命令前添加上@

    再次执行时就无输出了。

     在执行make时,带入make参数“-n”或“--just-print”参数,命令只会显示不会执行,用于调试自己写的makefile文件

    而 make 参数“-s”或“--slient”则是全面禁止命令的显示。

    5.2命令执行

    当依赖目标新于目标时,也就是当规则的目标需要被更新时,make 会一条一条的执行其后的命令。而当需要让一条命令的结果应用到下一条命令时,需要用分号隔开而不是另起一行。

    示例一: exec:     cd /home/hchen     pwd 示例二: exec:     cd /home/hchen;pwd

    其中示例一第一条命令不起作用。正确写法如示例二。

    5.3命令出错

        有些时候,命令的出错并不表示就是错误的。例如用mkdir创建一个文件,真正的目的是保证该目录的存在而不是当有该目录时创建失败退出makefile,可以通过在命令前加一个“-”(Tab键之后),无论命令成功与否都认为是成功的,继续执行之后命令。如:

    clean:     -rm-f*.o

        还有一个全局的办法是,给 make 加上“-i”或是“--ignore-errors”参数,那么,Makefile 中所有命令都会忽略错误。而如果一个规则是以“.IGNORE”作为目标的,那么这个规则中的所有命令将会忽略错误。

         make 的参数的是“-k”或是“--keep-going”,这个参数的意思是,如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。

    5.4嵌套执行make

        在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的 Makefile,这有利于让我们的 Makefile 变得更加地简洁,而不至于把所有的东西全部写在一个 Makefile 中,这样会很难维护我们的 Makefile,这个技术对于我们模块编译和分段编译有着非常大的好处。

        直接看实例:工程文件如下

    可以看主makefile文件内容如下

    CC =gcc TOP_DIR :=$(PWD) INCLUDE :=$(TOP_DIR)/headers SRC_DIR :=$(TOP_DIR)/src APP_DIR :=$(TOP_DIR)/app VPATH := $(SRC_DIR):$(APP_DIR) CFLAGS = -I $(INCLUDE) export CC VPATH CFLAGS .PHONY:all clean all: $(MAKE) -C $(SRC_DIR) $(MAKE) -C $(APP_DIR) clean: $(MAKE) -C $(SRC_DIR) clean $(MAKE) -C $(APP_DIR) clean

    其中$(MAKE) -C $(SRC_DIR)是到对应的子目录下执行make,使用$(MAKE)是为了方便以后可以添加适当的make参数,-C参数是在进入或者退出目录时打印出来。

    如下

    export的作用是将变量传递给子目录下的Makefile文件以供它们使用。

    看app下面的makefile文件

    OBJ = test.o $(OBJ):%.o:%.c $(CC) -c $(CFLAGS) $< -o $@ %.d:%.c @set -e;rm -f $@;\ $(CC) -MM $(CFLAGS) $< > $@.$$$$;\ sed 's,\($*\)\.o[:]*,\1.o $@:,g' < $@.$$$$ > $@;\ rm -f $@.$$$$\ -include $(OBJ:.o=.d) .PHONY:clean clean: rm $(OBJ) *.d *.d.* -f

    ,其实这么写是有错误的,因为$(OBJ)是第一目标,当本身只有一个目标的时候没问题,但是有两个.o文件的时间只会生成一个.o文件

    src下面的makefile文件如下

    OBJ =main.o fun.o .PHONY:all all:$(OBJ) $(OBJ):%.o:%.c $(CC) -c $< -o $@ $(CFLAGS) %.d:%.c @set -e;rm -f $@;\ $(CC) -MM $(CFLAGS) $< > $@.$$$$;\ sed 's,\($*\)\.o[:]*,\1.o $@:,g' < $@.$$$$ > $@;\ rm -f $@.$$$$\ -include $(OBJ:.o=.d) .PHONY:clean clean: rm $(OBJ) *.d *.d.* -f

    此时,$(OBJ)含有两个.o文件,需要用一个伪目标才可以正确生成,这里面的$(CFLAGS)由主makefile继承过来。

    执行过make之后的工程目录如下:

    5.5定义命令包

    Makefile 中出现一些相同命令序列,那么我们可以为这些相同的命令序列定义一个变量。定义这种命令序列的语法以“define”开始,以“endef”结束,如:

    define run-yacc yacc $(firstword $^) mv y.tab.c $@ endef

    这里,“run-yacc”是这个命令包的名字,其不要和 Makefile 中的变量重名。在“define”和“endef”中的两行就是命令序列。这个命令包中的第一个命令是运行 Yacc 程序,因为 Yacc 程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字。还是把这个命令包放到一个示例中来看看吧。

    foo.c:foo.y     $(run-yacc)

    我们可以看见,要使用这个命令包,我们就好像使用变量一样。在这个命令包的使用中,命令包“run-yacc”中的“$^”就是“foo.y”,“$@”就是“foo.c”(有关这种以“$”开头的特殊变量,我们会在后面介绍),make 在执行命令包时,命令包中的每个命令会被依次独立执行。类似于C语言中的宏定义函数。

     

     

    最新回复(0)