一、多个源文件带来的问题
在编写c/c++测试程序时,我们习惯每次修改一处代码,然后就马上编译运行来查看运行的结果。这种编译方式对于小程序来说是没有多大问题的,可对于大型程序来说,由于包含了大量的源文件,如果每次改动一个地方都需要编译所有的源文件,这个简单的直接编译所有源文件方式对程序员来说简直是噩耗。
我们看一个例子:
// main.c #include "a.h" // 2.c #include "a.h" #include "b.h" // 3.c #include "b.h" #include "c.h"
如果程序员只修改了头文件c.h,则源文件main.c和2.c都无需编译,因为它们不依赖这个头文件。而对3.c来说,由于它包含了c.h,所以在头文件c.h改动后,就必须得新编译。
而如果改动了b.h可是忘记编译了2.c,那么最终的程序就可能无法正常工作。
make 工具就是为了解决上述问题而出现的,它会在必要时重新编译所有受改动影响的源文件。
二、make 命令
make命令本身支持许多选项,最常用的是-f选项。如果我们直接运行
make
那么make命令会首先在当前目录查找名为makefile的文件,如果找不到,就会查找名为Makefile的文件。
为了指示make命令将哪个文件作为makefile文件,可以使用 -f 选项:
make -f Makefile1
三、makefile 文件
上面提到makefile文件,那么什么是makefile文件呢?
make命令功能虽然十分强大,但是光凭其自身无法了解如何构建应用程序的。这时,makefile就出来了,它告诉make应用程序如何构建的。make命令和makefile文件的结合提供了一个在管理项目的十分强大的工具,它们不仅用于控制源文件的编译,而且还提供了将应用程序安装到目标目录等其他功能。
3.1 依赖关系
依赖关系定义了应用程序里面每个文件与其他源文件之间的关系。例如在上面的例子中,我们可以定义最终应用程序依赖于目标文件main.o,2.o和3.o。同样,main.o依赖于main.c和a.h,2.o依赖于2.c,a.h和b.h,3.o依赖于3.c,b.h和c.h。
在makefile文件中,依赖关系的写法是:先写目标的名称,然后紧跟一个冒号,接着是空格或者制表符tab,最后是用空格或者制表符tab隔开的文件列表。上面的例子的依赖关系如下:
myapp: main.o 2.o 3.o main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h
这组依赖关系形成一个层次结构,展示了源文件之间的关系。例如,如果源文件b.h发生改变,就需要重新编译2.o和3.o,接下来还需要重新编译myapp。
3.2 规则
makefiel文件中的规则定义了目标的创建方式。在上面的例子中,我们使用gcc -c 2.c创建2.o。这个gcc命令即是目标2.o的创建方式,也即是规则。
在makefile文件中,规则都必须以tab开头。
在源文件所在的目录下创建Makefile1文件,其内容如下。
myapp: main.o 2.o 3.o gcc -o myapp main.o 2.o 3.o main.o: main.c a.h gcc -c main.c 2.o: 2.c a.h b.h gcc -c 2.c 3.o: 3.c b.h c.h gcc -c 3.c
三个头文件a.h,b.h,c.h内容都为空,源文件的内容如下:
/* main.c */ #include <stdlib.h> #include "a.h" extern void function_two(); extern void function_three(); int main() { function_two(); function_three(); exit(EXIT_SUCCESS); }
/* 2.c */ #include <stdio.h> #include "a.h" #include "b.h" void function_two() { printf("function two\n"); }
/* 3.c */ #include <stdio.h> #include "b.h" #include "c.h" void function_three() { printf("function three\n"); }
执行make命令,:
$ make -f Makefile1 gcc -c main.c gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o
运行应用程序:
$ ./myapp function two function three
从输出可以说明应用程序已被正确构建。
如果改变b.h头文件,makefile能够正确处理这一变化,只有2.c和3.c发生重新编译:
$ touch b.h $ make -f Makefile1 gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o
3.3 注释
makefile文件使用#来表示注释,一直延续到这一行的结束。
3.4 宏
不同的平台下可能使用不同的编译器,不同的环境(例如开发与线上环境)也可能使用不同的编译器选项,为了便于修改makefile这些可变的参数,我们可以使用宏来实现makefile。
makefile引用宏定义的方法为$(MACRONAME)。我们来看如何使用宏来改写上面的makefile文件。
all: myapp # 编译器 CC = gcc # include的搜索路径 INCLUDE = . # 编译器参数 CFLAGS = -g -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
我们习惯在makefile文件中将第一个目标定义为all,然后再列出其他从属的目标,上面的makefile也遵循这个约定。
运行make命令:
$ make -f Makefile2 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o
同样也正确构建了应用程序myapp。
3.5 多个目标
makefile文件除了定义编译的目标外,还可以定义其他的目标。例如,增加一个clean选项来删除不需要的目标文件,增加一个install选项来将编译成功的应用程序安装到另一个目录下,等等。
all: myapp CC = gcc INSTDIR = /usr/local/bin INCLUDE = . CFLAGS = -g -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c clean: -rm main.o 2.o 3.o install: myapp @if [ -d $(INSTDIR) ]; \ then \ cp myapp $(INSTDIR);\ chmod a+x $(INSTDIR)/myapp;\ chmod og-w $(INSTDIR)/myapp;\ echo "Install in $(INSTDIR)";\ else \ echo "sorry, $(INSTDIR) does not exist";\ fi
上面的makefile文件有几点需要注意的。
(1)特殊目标all只指定了myapp这个目标,因此,在执行make命令时未指定目标,它的默认行为就是创建目标myapp。
(2)目标clean用来html" target="_blank">测试编译过程中产生的中间文件。
(3)目标install用于将应用程序安装到指定目录,它依赖于myapp,即执行install前须先创建myapp。install目标由shell脚本组成,由于make命令在执行规则时会调用一个shell,并且会针对每个规则使用一个新的shell,所以必须在上面每行代码的结尾加上一个\,让所有的shell脚本都处于同一行。
脚本以@开头,说明make在执行这些规则之前不会在标准输出显示命令本身。
创建myapp:
$ make -f Makefile3 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o
将myapp安装到指到目录:
$ make -f Makefile3 install Install in /usr/local/bin
然后可以直接执行myapp:
$ myapp function two function three
删除中间文件:
$ make -f Makefile3 clean rm main.o 2.o 3.o
以上这篇基于make命令与makefile文件详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持小牛知识库。
make和Makefile GNU make(简称make)是一种代码维护工具,在大中型项目中,它将根据程序各个模块的更新情况,自动的维护和生成目标代码。 make命令执行时,需要一个 makefile (或Makefile)文件,以告诉make命令需要怎么样的去编译和链接程序。首先,我们用一个示例来说明makefile的书写规则。以便给大家一个感兴认识。这个示例来源于gnu的make使用手册,在
编译型语言在每次执行之前都要进行编译。这在有人眼里是优势,因此编译过程中可以进行一些语法等信息的检查,避免一些初级的错误。同时编译出来的代码可以有更好的运行速度。但是在有些人眼里这就是灾难。其实不是有时候不是因为别的什么原因,而是因为你输入的编译命令可能长的的需要好几行才能输入完。中间要是修改起来简直就是不可能。 所以,我们有了一个新的工具 make。 1. make 工具 make 工具是用来构
每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以[Tab]键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。 我们在UNIX下可能会使用不同的Shell,但是make的命令默认是被“/bin/sh”——UNIX的标准Shel
本文向大家介绍Android Sqlite命令详解(基本命令),包括了Android Sqlite命令详解(基本命令)的使用技巧和注意事项,需要的朋友参考一下 在Android开发中SQLite起着很重要的作用,网上SQLite的教程有很多很多,不过那些教程大多数都讲得不是很全面。本人总结了一些SQLite命令,借着论坛的大赛,跟大家分享分享的。 一.SQLite的介绍 1.SQLite简介 SQ
我的目的是通过sound.c做声波文件的准备,安排训练过程,测试过程。编译darknet时出错。需要你的帮助! make:gcc:找不到命令makefile:175:目标'obj/sound.o'的制作方法失败make:***[obj/sound.o]错误127 UBUNTU LTS 18.04 CUDA 11.1
本文向大家介绍Linux基础命令last 命令实例详解,包括了Linux基础命令last 命令实例详解的使用技巧和注意事项,需要的朋友参考一下 Linux last命令用于显示系统开机以来获是从每月初登入者的讯息。 使用权限:所有使用者。 last 显示以前登录过的用户信息,last指令会搜索/var/log/wtmp文件(或者是经过-f选项指定的文件),然后列出文件中所有的用户信息。