makefile简介及编写之一

Makefile 是 Linux 下程序开发的自动化编译工具,用以识别编译目标源文件及其依赖关系,并且有着高效的编译效率。每次执行
make 时,就能够自动寻找 Makefile(makefile)文件,执行编译工作。Makefile拥有很多复杂的功能,为了简化问题的复杂性,本文仅和大家讨论针对单目录下的C/C++项目开发,如何写一个通用的
Makefile。

关于程序的编译和链接

在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。

编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive
File,也就是 .a 文件。

总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object
File.

Makefile
介绍

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。

首先,我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件,和3个头文件,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是:

1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。

2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。

3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。

Makefile的规则

一个Makefile中通常包含下面内容:1、需要由make工具创建的目标体(target),通常是目标文件或可执行文件。2、要创建的目标体所依赖的文件(dependency)。3、创建每个目标体时需要运行的命令(command)。
基本语法规则如下:

注意:如果“command”不和目标文件所在一行时,在command前要加 Tab

简单示例

下面以一个简单的程序为例,包括主程序(hello.c)、函数代码(function.c)、头文件(function.h),内容如下:

主函数hello.c:

#include <stdio.h>
#include "function.h"
int main()
{
    fun1();
    fun2();
    fun3();
    return 0;
}

函数function.c:

#include<stdio.h>
int fun1()
{
    printf("This is first function!\n");
    return 0;
} 

int fun2()
{
    printf("This is second function!\n");
    return 0;
} 

int fun3()
{
    printf("This is third function!\n");
    return 0;
}

头文件function.h:

#ifndef _FUN_H
#define _FUN_H

int fun1(void);
int fun2(void);
int fun3(void);

#endif

一般的,我们会使用

gcc -o hello hello.c function.c -I.

来编译。“-I.”是指 gcc 在当前目录(.)下寻找头文件。如果不用 makefile,在“测试-修改-调试”过程中,我们必须不停地在终端中按上下键来寻找最后的那条编译指令。这种编译方法有两个缺陷:

  1. 当你把编译指令丢失或者换电脑的时候,这样效率会很低;
  2. 当我们只修改了一个.c文件时,每一次都将必须重新编译所有的文件,非常耗时,不划算。

所以我们需要用 Makefile 文件来统一管理这种编译规则,这样只要我们在目录下敲入“make”命令,就可以编译了。本例最简单的 Makefile 写法:

hello: hello.c function.c
    gcc -o hello hello.c function.c -I.

注意:第一行中并没有任何参数,只是在冒号(:)后列出编译中所需的文件,当第一行中的任何文件中更改时,make 就知道 hello  需要重新编译了。还有一点非常重要:gcc 前面必须有一个Tab,在任何指令之前都要有一个Tab,不然make就会罢工的。

现在我们已经解决了问题1,不用上下按箭头了,但是对于问题2依旧没有很好地解决。

为了让事情变得更通用,我们将 Makefile 改成:

TARGET = hello
CC = gcc
CFLAGS = -I.

$(TARGET): hello.c function.c
   $(CC) -o $(TARGET) hello.c function.c $(CFLAGS)

这里我们新定义了三个变量 TARGET、CC 和 CFLAGS 。TARGET是目标文件名,CC是编译器,CFLAGS是编译用的参数。make 会先分别编译 .c 文件,然后生成可执行文件 hello。

这种形式的makefile在小项目中非常有效,但是有一个遗憾:include 头文件的变动。如果我们修改了 function.h 文件,make是不会重新编译 .c 文件的,事实上我们需要重新编译。为了解决这一问题,我们必须告诉 make 所有的 .c 文件依赖于 .h 文件。

一般在书写Makefile时,各部分变量引用的格式如下:

  1. make变量(Makefile 中定义的或者是 make 的环境变量)的引用使用“$(VAR)”格式,无论“VAR”是单字符变量名还是多字符变量名。
  2. 出现在规则命令行中shell变量(一般为执行命令过程中的临时变量,它不属于Makefile变量,而是一个shell变量)引用使用shell的“$tmp”格式。
  3. 对出现在命令行中的 make 变量同样使用“$(CMDVAR)” 格式来引用。

自动变量

自动变量可以让 make 看到一个.o文件,自动把对应的 .c 文件加到依赖文件中,这样 Makefile 就简化了。我们可以在 Makefile 中增加一条规则:

TARGET = hello
CC = gcc
CFLAGS = -I.
DEPS = function.h

%.o: %.c $(DEPS)
    $(CC) -c -o [email protected] $< $(CFLAGS)

$(TARGET): hello.c function.c
    $(CC) -o [email protected] $^ $(CFLAGS)

首先定义 DEPS,声明 .c 文件所依赖的 .h 文件。然后我们定义一条规则,为所有的 .c 文件生成一个 .o 文件。最后再定义生成目标文件。其中,

  • -c:意味着产生object文件;
  • [email protected]:目标文件(%.o),即冒号(:)的左边;
  • $^:所有的依赖文件(%.c),即冒号(:)的右边;
  • $<:第一个依赖文件。

在这个版本中,我们把所有 object 文件作为 OBJ 的一部分:

TARGET = hello
CC = gcc
CFLAGS = -I.
DEPS = function.h
OBJ = hello.o function.o

%.o: %.c $(DEPS)
    $(CC) -c -o [email protected] $< $(CFLAGS)

$(TARGET): $(OBJ)
    $(CC) -o [email protected] $^ $(CFLAGS)

这一步中会生成两个
.o 文件和目标文件,有时候我们需要清除它们,就要加入一个伪目标:clean,在最后加上这样的两行:

clean:
    rm -f $(TARGET) *.o

有的目标可能没有依赖,只有动作(指定的命令)。比如上面的“clean”伪目标,没有依赖,只有命令。它所指定的命令用来删除
make 过程产生的中间文件,我们使用”make clean“就可以实现清理工作。

时间: 2024-08-06 20:36:41

makefile简介及编写之一的相关文章

Makefile简介

1.源程序的编译在Linux下面,如果要编译一个C语言源程序,我们要使用GNU的gcc编译器. 下面我们以一个实例来说明如何使用gcc编译器.假设我们有下面一个非常简单的源程序(hello.c):int main(int argc,char **argv){printf("Hello Linux\n"):}要编译这个程序,我们只要在命令行下执行:gcc -o hello hello.cgcc 编译器就会为我们生成一个hello的可执行文件.执行./hello就可以看到程序的输出结果了.

makefile学习之路——makefile简介

一.make简介 make是一个指令工具,它解释makefile中的指令或者说规则.makefile文件描述了整个工程中所有文件的**编译顺序,编译规则**.Makefile也有自己的编写规则,通常,我们所使用的IDE都会生成相应的makefile,然后再根据makefile来进行编译,只是这些操作是由IDE来完成,我们只需要点击一个编译按钮. 二.为什么要使用make 现在可以在GitHub上看到,很多的开源项目,在编译的时候,都是使用make来完成的,也就是说,都有其对应的makefile.

Makefile 简介(一)

Makefile/makefile 简介 makefile 规则 target :prerequisites command target: 目标文件 prerequisites: 依赖文件 command: 执行命令 命令执行之前先检查依赖 依赖存在:执行命令 依赖不存在: 向下检测其他的规则,看看是否有一个规则是用来生成这个依赖,如果找到, 执行该规则中的命令 检测更新 在执行规则中的命令的时候, 会比较 目标和依赖文件时间 如果依赖的时间比目标的时间晚,需要重新生成目标 如果依赖的时间比目

Makefile的简单编写

当我们的工程越来越大的以后,每次都使用命令编译显得非常的麻烦.所以我们需要一个自动化编译的工具来帮助我们编译.在我们使用源码安装软件的时候都会有make,make install等操作,这个就是使用Makefile来进行自动化编译的工作的. Makefile也是一个脚本文件和shell脚本非常的相似,但是也有很多的地方不一样. 程序编译通常会有一下几个步骤,先是预编译,然后将C语言代码编译成汇编,然后在将汇编编译成二进制文件.o文件,然后将所有的.o文件连接起来就得到了可执行文件. 在编写Mak

Linux平台Makefile文件的编写基础篇(转)

目的:       基本掌握了 make 的用法,能在Linux系统上编程.环境:       Linux系统,或者有一台Linux服务器,通过终端连接.一句话:有Linux编译环境.准备:       准备三个文件:file1.c, file2.c, file2.h       file1.c:              #include <stdio.h>              #include "file2.h"              int main()  

Linux平台Makefile文件的编写基础入门(课堂作业)

根据老师的要求,写一个超简单的makefile准备:       准备三个文件:file1.c, file2.c, file2.h       file1.c: #include "file2.h" int main() { printf("print file1$$$$$$$$$$$$$$$$$$$$$$$$\n"); File2Print(); return 0; } file2.h: #include <stdio.h> #ifndef FILE2

Linux学习之Makefile文件的编写

转自:http://goodcandle.cnblogs.com/archive/2006/03/30/278702.html 目的:       基本掌握了 make 的用法,能在Linux系统上编程.环境:       Linux系统,或者有一台Linux服务器,通过终端连接.一句话:有Linux编译环境.准备:       准备三个文件:file1.c, file2.c, file2.h       file1.c:              #include <stdio.h>    

[转]makefile文件的编写规则及实例

http://xueqi.iteye.com/blog/1567866 1.一个简单的makefile例子 假设一个程序有两个文件file1.c,file2.c,每个文件都包含head.h,生成file可执行文件 file:file1.o file2.o                  附属行(文件的依存关系) gcc -o file1.o file2.o            命令行 file1.o:file1.c head.h gcc -c file1.c file2.o:file2.c

浅谈Linux下Makefile编写

浅谈Linux下Makefile的编写 前言:本文简要介绍Makefile文件的编写规范,结合具体项目中的应用进行讲解. 具体代码地址: https://github.com/AnSwErYWJ/DogFood/blob/master/Makefile 简介 Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作.而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关