一、什么是makefile

百度百科:Linux 环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格的专业程序员,至少不能称得上是Unix程序员。在 Linux(unix)环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile文件的编写。

所要完成的Makefile文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。尽管看起来可能是很复杂的事情,但是为工程编写Makefile的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile。编译整个工程你所要做的事就是在shell提示符下输入make命令。整个工程完全自动编译,极大提高了效率

make是一个命令工具,它解释Makefile 中的指令。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile在绝大多数的IDE开发环境中都在使用,已经成为一种工程的编译方法。

总之,原来我们在编译文件时每次都需要在命令行上输入许多命令,而如果正确编写了makefile文件之后,便可以实现自动化编译,大大提高了效率。

二、创建makefile文件

在你需要编译文件的地方,创建一个文件名为makefile的文件,没有拓展名。输入如下内容:

start:
echo "hello world"
# 注意:缩进必须为tab不能为空格

保存后,运行make命令,程序会自动寻找makefile文件,如果文件名不叫makefile的话,可以使用make -f [文件名]的形式,但为了规范建议不要这么做。

$ make

其中,start是一个标签,标签下是要运行的linux命令,以tab缩进。可以编写多个标签,make默认执行第一个标签。

运行结果:

echo "hello world"
hello world

make会把执行的命令打印出来,如果不想打印命令,可以在命令前面加上@:

@echo "hello world"

三、使用make编译程序

以C++为例,编写一个简单的C++文件,命名为main.cpp:

#include<iostream>
using namespace std; int main()
{
cout << "Hello world" << endl;
return 0;
}

将makefile修改为:

start:
g++ main.cpp -o main
@echo "--------ok--------"

运行结果:

g++ main.cpp -o main
--------ok--------

四、生成目标文件之后再生成程序

为了之后可以链接多个程序文件,需要先汇编成目标文件,我们这么修改makefile:

start: main.o
g++ main.o -o main
@echo "--------ok--------"
main.o:
g++ -c main.cpp -o main.o

我们编写了两个标签,由于默认只运行第一个标签,所以main.o标签不会运行,但是我们在运行start时如果找不到main.o文件,make将报错,那么就需要在标签后面写上所需要的文件或者目标,make会自动找到main.o标签并执行,运行结果如下:

g++ -c main.cpp -o main.o
g++ main.o -o main
--------ok--------

再次运行的话,由于目录下已经存在main.o文件了,就不会执行main.o标签下的命令:

g++ main.o -o main
--------ok--------

为了方便删除生成的目标文件和程序,可以编写一个clean标签:

start: main.o
g++ main.o -o main
@echo "--------ok--------"
main.o:
g++ -c main.cpp -o main.o
clean:
rm main.o main

通过make [标签]的形式,可以直接运行对应的标签,比如:

$ make clean

五、使用变量

为了方便维护makefile文件,可以使用变量,比如说把g++定义为变量,方便修改编译器:

CC=g++

start: main.o
$(CC) main.o -o main
@echo "--------ok--------"
main.o:
$(CC) -c main.cpp -o main.o
clean:
rm main.o main

如上所示,使用$(变量名)的形式就可以调用变量。

再进一步,将源文件,目标文件、程序文件都改成变量:

#编译器
CC=g++
#源文件
SRCS=main.cpp
#目标文件
OBJS=$(SRCS:.cpp=.o)
#可执行文件
EXEC=main start: $(OBJS)
$(CC) -o $(EXEC) $(OBJS)
@echo "---------ok--------" $(OBJS):
$(CC) -c $(SRCS) -o $(OBJS)
clean:
rm $(EXEC) $(OBJS)

其中,$(SRCS:.cpp=.o)代表将SRCS中cpp文件的后缀改成o。CC、SRCS、OBJS、EXEC都是比较规范的写法,一般变量名为大写。

六、使用SUFFIXES

为了让make知道如何从.cpp文件得到.o文件,可以定义后缀依赖,在makefile第一行写:

.SUFFIXES:.cpp .o

这样make就知道.cpp和.o是后缀,当需要.o文件时,会自动寻找.cpp.o标签:

.SUFFIXES:.cpp .o
#后缀之间有空格 CC=g++
SRCS=main.cpp
OBJS=$(SRCS:.cpp=.o)
EXEC=main start: $(OBJS)
$(CC) -o $(EXEC) $(OBJS)
@echo "--------ok--------" #定义后缀依赖,后缀之间没空格,前者为前提,后者为目标
.cpp.o:
$(CC) -c $< -o $@
# $< 第一个依赖文件的名称
# $@ 目标的完整名称 clean:
rm $(EXEC) $(OBJS)

运行结果:

g++ -c main.cpp -o main.o
g++ main.o -o main
--------ok--------

这样当有很多文件需要编译时,我们就不需要自己编写从.cpp到.o文件的标签。

七、链接多个文件

定义一个test.h:

void output();

定义一个test.cpp:

#include<iostream>

void output()
{
std::cout<<"call output!"<<std::endl;
}

修改main.cpp:

#include<iostream>
#include "test.h"
using namespace std; int main()
{
output();
return 0;
}

在使用g++时,并不能通过只编译主程序来链接多个文件,我们需要将多个cpp文件编译成目标文件后再进行链接,如果文件很多的话非常繁琐,工作效率很低。

$ g++ -c main.cpp -o main.o
$ g++ -c test.cpp -o test.o
$ g++ -o main main.o test.o

编写makefile之后,这些都可以自动完成,我们只需要在SRCS变量处添加多个CPP文件即可:

SRCS=main.cpp\
test.cpp

修改之后的makefile:

.SUFFIXES:.cpp .o

CC=g++

SRCS=main.cpp\
test.cpp
OBJS=$(SRCS:.cpp=.o)
EXEC=main start: $(OBJS)
$(CC) -o $(EXEC) $(OBJS)
@echo "--------ok--------"
.cpp.o:
$(CC) -c $< -o $@
clean:
rm $(EXEC) $(OBJS)

由于我们之前已经定义了.cpp.o规则,所以make会自动将所有.cpp文件编译为.o文件。

g++ -c main.cpp -o main.o
g++ -c test.cpp -o test.o
g++ -o main main.o test.o
--------ok--------

八、其他

为了节省编译时间,make会根据cpp文件的修改时间,自动判断需不需要重新编译,如果不需要重新编译,链接时还是之前编译的.o文件。

所以一般不需要使用make clean,然而如果只修改了头文件的话也是不会重新编译的,可以把头文件加到依赖中,这样检测到头文件修改的话就会重新编译,不用每次都make clean了。

本文只是makefile的一点皮毛,想要掌握还需要学习很多。

makefile从0到1的更多相关文章

  1. Makefile教程

    Makefile学习教程: 跟我一起写 Makefile 0 Makefile概述 0.1 关于程序的编译和链接 1 Makefile 介绍 1.1 Makefile的规则 1.2 一个示例 1.3 ...

  2. Makefile第四讲:include 引用其它makefile文件

    main.cpp #include "classes/fun.h" int main() { Test::display("Hello makefile"); ...

  3. Makefile第一讲:一个简单的Makefile

    摘要 假定你对linux已经比较的熟悉,假定你编程已经稍有经验,本文不会对文章作出太多基础性解释,看不懂莫怪,只当作给学习的朋友一个引导思路,我也是一个初学者,边学边写,将学会的教给大家,文章有错误之 ...

  4. Makefile第二讲:打印出内容和使用变量

    摘要 `@echo "开始生成最终执行文件,请稍候..."`这一句便是将一条信息输出到终端,为何前边有个`@`符号呢?有了这个符号该命令本身就不会输出到终端(不理解,自己去掉或者加 ...

  5. 编写高质量的Makefile

    源地址 :http://blog.csdn.net/maopig/article/details/6801749 一.前言 回想自己的第一个Makefile,是这个样子的 CODE hello:hel ...

  6. u-boot for tiny210 ver1.0(by liukun321咕唧咕唧)

     新版本下载: 下面的链接提供了较新版本的源码 ver4.0源码下载:u-boot for tiny210 ver4.0 ver3.1源码下载: u-boot for tiny210 ver3.1 v ...

  7. python网络编程【一】

    TCP/IP 是标准的协议,它可以使用世界范围内的计算机通过Internet或本地的网络通信 1.编写一个TCP客户端程序 #!/usr/bin/env python import socket, s ...

  8. urllib源码简单分析

    对下面这段代码做分析 import urllib params = urllib.urlencode({'wd': 'python'}) f = urllib.urlopen("http:/ ...

  9. VS2010 C++环境下DLL和LIB文件目录及名称修改

    VS2010 C++环境下DLL和LIB文件目录及名称修改 转自:http://blog.csdn.net/archielau/article/details/8507581 DLL工程,Debug版 ...

随机推荐

  1. LeetCode 826. Most Profit Assigning Work

    原题链接在这里:https://leetcode.com/problems/most-profit-assigning-work/ 题目: We have jobs: difficulty[i] is ...

  2. packr 方便的潜入静态资源文件到golang 二进制文件中

    类似的工具以前有介绍过statik,今天使用的工具是packr 也是很方便的golang tools 安装 go get -u github.com/gobuffalo/packr/packr 或者我 ...

  3. 通过HttpServletRequest重写+filter 添加header

    问题说明 需要做的事情比较简单,就是通过filter 重写httpservletrequest ,同时给予request 添加header 主要是通过HttpServletRequestWrapper ...

  4. Tomcat启动问题:严重[main] org.apache.catalina.core.AprLifecycleListener.init An incompatible version...

    今天观察tomcat启动日志,有一些以前没注意到的信息: 严重 [main] org.apache.catalina.core.AprLifecycleListener.init An incompa ...

  5. JS实现Base64编码、解码,即window.atob,window.btoa功能

    window.atob(),window.btoa()方法可以对字符串精选base64编码和解码,但是有些环境比如nuxt的服务端环境没法使用window,所以需要自己实现一个base64的编码解码功 ...

  6. Hdu 5093 Battle Ship

    每个海面要么放要么不放,因此可以用二分图匹配, 考虑把同一行内的能互相看到的点放到一个行块里,同一列内能看到的点放到一个列块里,然后每一个行块都可以和该行块里所有海面的列块连边,选了这个行块,就必须选 ...

  7. Bacteria(优先队列)

    题目链接:http://codeforces.com/gym/101911/problem/C 问题简述:给定n个细胞以及每个细胞的大小,相同的细胞能进行融合,如果能融合到只剩1个细胞则输出需要额外增 ...

  8. Koa 操作 Mongodb 数据库

    node-mongodb-native的介绍 使用基于官方的 node-mongodb-native 驱动,封装一个更小.更快.更灵活的 DB 模块, 让我们用 nodejs 操作 Mongodb 数 ...

  9. 刷题记录:[SUCTF 2019]EasySQL

    目录 刷题记录:[SUCTF 2019]EasySQL 一.涉及知识点 1.堆叠注入 2.set sql_mode=PIPES_AS_CONCAT;将||视为字符串的连接操作符而非或运算符 3.没有过 ...

  10. 20189220 余超《Linux内核原理与分析》第六周作业

    系统调用的三层机制 实验过程 1.克隆MenuOS rm menu -rf //强制删除当前menu git clone http://git.shiyanlou.com/mengning/menu. ...