Makefile是一个自动化的编译工具,关系到整个工程的编译规则,极大的提高了软件开发的效率。

    (1)Makefile的编译规则

//Makefile 也可以写作 makefile
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
)如果这个工程的头文件被改变了,那么我们需要编译引用了这个头文件的所有C文件,并链接目标程序。

    (2)Makefile的书写规则

规则的三个要素:目标、依赖、命令

 //Makefile格式 
target ... : prerequisites ...
command
//解释
target:目标文件,可以是object file,也可以是可执行文件,还可以是一个标签;
prerequisites:生成target所需要的目标或者文件;
command:任意shell命令

    (3)Makefile的工作原理

//当执行make命令生成目标文件时
.make命令使用时会在当前目录下寻找名为Makefile或者makefile的文件;
.如果找到,它将会把文件中第一个target当作最终的目标文件;
.如果target已然存在,并且它所依赖的所有文件的修改时间都没更改,则返回;否则执行后面的内容;
.一层一层往前推,类似一个堆栈,直到到达所依赖的文件是.h和.c或者.cpp文件为止,此时便可以链式推导生成最终的目标文件了;

    (4)gcc和g++命令概述

    1)gcc和g++简介

    GCC(GNU Compiler Collection)    GNU编译器套件

gcc和g++均是GCC的一部分,gcc是GNU的c编译器,g++是GNU的c++编译器;

gcc将后缀名为.c的文件当作c程序,将后缀名为.cpp的文件当作c++程序;

g++将后缀名为.c和cpp的均当作c++程序;

    2)区别

g++和gcc均可以编译链接c或者c++程序,使用方法有稍微的区别,g++编译链接c程序时可能调用gcc;gcc编译链接c++时,由于c++是c的超集,需要指定使用c++的动态库libstdc++.so;

范例如下,是等价的

// 存在一个main.cpp
g++ main.cpp -o test
// 链库的-l参数必须放置在源码之后
gcc main.cpp -lstdc++ -o test
gcc main.cpp -o test -lstdc+

    3) GNU的编译步骤

.预处理(Preprocessing)
//由预处理器cpp完成,将.cpp源文件预处理为.i文件。
g++ -E test.cpp -o test.i .编译(Compilation)。由编译器cc1plus完成,将.i文件编译为.s的汇编文件。使用-S选项,只进行编译而不进行汇编,生成汇编代码。
g++ -S test.i -o test.s .汇编(Assembly)。由汇编器as完成,将.s文件汇编成.o的二进制目标文件。
g++ -c test.s -o test.o .链接(Linking)。由链接器ld,将.o文件连接生成可执行程序。
g++ test.o -o test.out
g++ test.o -o test

    (5)Makefile使用简介

    1)变量的定义和使用

#定义编译器
cc = gcc
或者
cc = g++
#定义编译参数
-w 不显示任何警告信息 -W 只显示错误警告信息 -Wall 显示所有警告信息
CFLAG = -g -Wall -W
#定义待链接的库 -L后指明待链接的库所在的路径 -l参数待链接的库名
LIBRARY + = -L /usr/lib/ -lstdc++
#添加宏定义
DEBUG = MYDEF
DEF = -D$(DEBUG)
#使用 -o参数建议放在编译命令最后,否则可能会将源文件删除 -g参数必须放置在-o参数之前
$(cc) $(CFLAG) $(DEF) $(源文件) -o $(目标文件) $(LIBRARY)

在变量的使用过程中,分为两种,递归展开式变量和直接展开式变量

//递归展开式变量
A = $(B)
B = $(C)
C = ME
则 A = ME,其优点是前面使用的变量可以使用后续定义的变量的值,缺点是有可能陷入无限循环
//直接展开式变量
A = aaa
B := $(A)bbb
A = ccc
则 B的值为 aaabbb,后续定义的A的值与其无关
如果写法为
A = aaa
B = $(A)bbb
A = ccc
则最终 B的值为 cccbbb

    2)$相关变量

$^    所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$@ 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合
$? 所有比目标新的依赖目标的集合。以空格分隔。
$< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$(@D) 表示"$@"的目录部分(不以斜杠作为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录) 。
$(@F) 表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)"
// 例1
%.o : %.c
gcc -c $< -o $@
把所以的c文件编译生成对应的o文件,$<代表每次取的c文件,$@代表每次c文件对应的目标文件
// 例2
main : main.o test.o test1.o test2.o
gcc -o $@ $^
把所有的o文件编译生成可执行的main文件,$^代表所以的依赖文件集合(main.o test.o test1.o test2.o),@代表目标文件(main)
// 例3
lib : test.o test1.o test2.o
ar r lib $?
把有更新的依赖文件重新打包到库lib中, 如果只有test1.o更新,则$?代表test1.o, 如果test.o test1.o都有更新,则$?代表test.o test1.o的集合。

    3)伪目标 clean build rebuild all

CC := gcc
Target := helloworld.out $(Target) : func.o main.o
$(CC) -o $(Target) main.o func.o main.o : main.c
$(CC) -c main.c -o main.o
func.o : func.c
$(CC) -c func.c -o func.o .PHONY : rebuild clean build rebuild : clean build build : $(Target)
# @echo "build"
clean :
# @echo "clean"
rm *.o $(Target)

在上述Makefile中,clean伪目标作用是将编译链接过程中所有生成文件全部删除,回到make执行的初始状态;build的作用是生成target,与make的作用相同;rebuild伪目标依赖于clean 和 build,当用户输入make rebuild时,实际上等效于make clean ,make build依此执行,build依赖于target,此时会将target重新生成,整个工程因此重新编译链接并生成。all伪目标适用于在同一个makefile中生成多个目标库文件时使用。

    4)函数

    GNU make提供了很多的函数,可以在Makefile文件中调用这些函数来进行文件名、变量以及命令等的处理。

() patsubst  主要对字符串进行运算和分析
用法:$(patsubst pattern,replacement,text)
功能:将text文本中出现的所有pattern替换为replacement
例子:$(patsubst %.c,%.o,a.c,b.o,c.c)
输出:a.o,b.o,c.o
() dir 主要用于获取文件的路径
用法: $(dir text)
功能: 将text中所有文件的对应目录输出
例子: $(dir main.cpp,libstdc++d)
输出 ./ , /usr/lib/
()notdir 抽取除去路径意外的其它信息
用法: $(notdir text)
功能: 去除text中所有包含的路径,只留下文件信息
例子: $(notdir /home/perfect/Mywork/C/main.c ./Makefile)
输出: main.c Makefile
() suffix 获得后缀名
用法:$(suffix text)
功能:将text中所有文件只留下后缀名
例子:$(suffix a.c,b.c)
输出 .c,.c
() addsuffix 给源目标文件添加前缀
用法:$(addprefix param,text)
功能:将text中每个源文件添加上合适的前缀后缀后输出
例子:$(addprefix -l,$(LIBS))
输出:完成的库名
() wildcard 扩展通配符
用法:$(wildcard PATTERN...)
功能:获取所有复合PATTERN格式的文件
例子:$(wildcard *.c)
输出:当前目录下所有的.c文件
复杂例子:$(patsubst %.c,%.o,$(wildcard *.c))
首先获取当前路径下所有的.c文件,然后将.c后缀名更改为.o后缀名并返回;

    5)include关键字

include命令用于将最新的子Makefile包含进当前Makefile文件,再根据当前Makefile对文件进行编译链接;适用于当系统过大时,Makefile复杂时进行拆分。

    (6)Makefile简单范例

//clac_test.h
#ifndef _CALC_TEST_H_
#define _CALC_TEST_H_
namespace test
{ int add(int a,int b); }
//calc_test.cpp
#include "calc_test.h"
namespace test
{
int add(int a,int b)
{
return a + b ;
}
}
//make_test.h
#ifndef _MAKE_TEST_
#define _MAKE_TEST_
#include <iostream>
namespace test
{
class MakeTest
{
public:
void run();
};
}
#endif
//make_test.cpp
#include "make_test.h"
#include "calc_test.h"
namespace test
{
void MakeTest::run()
{
int a = ;
int b = ;
std::cout<<test::add(a,b)<<std::endl;
}
}
//main.cpp
#include "string.h"
#include "make_test.h" using namespace std; int main()
{
test::MakeTest makeTest;// = new MakeTest();
makeTest.run();
return ;
}

    方法1:直接使用g++命令

g++ calc_test.cpp  make_test.cpp main.cpp -o test

    方法2:直接使用gcc命令

gcc calc_test.cpp make_test.cpp main.cpp -lstdc++ -o test

    方法3:全量的Makefile

test : main.o calc_test.o make_test.o
g++ main.o calc_test.o make_test.o -o test
main.o : main.cpp
g++ -c main.cpp -o main.o
calc_test.o : calc_test.cpp
g++ -c calc_test.cpp -o calc_test.o
make_test.o : make_test.cpp
g++ -c make_test.cpp -o make_test.o
main.cpp : make_test.h
make_test.cpp : make_test.h calc_test.h
calc_test.cpp : calc_test.h
.PHONY : clean
clean :
rm test *.o

    方法4:Makefile(隐晦推导)

test : main.o calc_test.o make_test.o
g++ main.o calc_test.o make_test.o -o test
main.o : make_test.h main.cpp
g++ -c main.cpp make_test.cpp
make_test.o : make_test.h calc_test.h
g++ -c make_test.cpp calc_test.cpp
calc_test.o : calc_test.h
g++ -c calc_test.cpp
.PHONY: clean
clean:
rm test *.o

    方法5:Makefile(变量定义)

objects = calc_test.o make_test.o main.o

test:$(objects)
gcc -o test $(objects) -lstdc++
$(objects):calc_test.h make_test.h
.PHONY:clean
clean:
rm test *.o

    (7)Makefile处理多目标文件

    1)单一Makefile利用伪目标 all

新建文件test.cpp 编译为 temp库

//test.cpp
#include <iostream> int main()
{
std::cout<<10<<std::endl;
return 0;
}
//Makefile
all : test temp
temp : test.cpp
gcc test.cpp -o temp -lstdc++
test : calc_test.cpp make_test.cpp main.cpp
g++ calc_test.cpp make_test.cpp main.cpp -o test
.PHONY : clean
clean :
rm test temp

    2)当有多个Makefile时,每个Makefile一个目标文件

新建rapidmain.cpp(当前路径下有rapidxml的所有头文件)

//rapidmain.cpp
#include <iostream>
#include "rapidxml_print.hpp"
#include "rapidxml_utils.hpp"
#include "rapidxml.hpp" using namespace std; int main()
{
rapidxml::xml_document<> doc;
rapidxml::xml_node<> *declaration = doc.allocate_node(rapidxml::node_declaration);
declaration->append_attribute(doc.allocate_attribute("version","1.0"));
declaration->append_attribute(doc.allocate_attribute("encoding","utf-8"));
doc.append_node(declaration); std::cout<<doc<<endl;
return ;
}

新建Makefile.rapidxml文件

rapid : rapidmain.cpp
g++ -I ./rapidxml/ rapidmain.cpp -o rapid
.PHONY : clean
rm rapid

新建Makefile.maketest文件

objects = calc_test.o make_test.o main.o

test:$(objects)
gcc -o test $(objects) -lstdc++
$(objects):calc_test.h make_test.h
.PHONY :clean
clean:
rm test *.o

方法1.新建总Makefile(采用伪目标和make命令)

all :
make -f Makefile.rapidxml
make -f Makefile.maketest
// make -C [路径名] 进入指定路径执行make命令

   方法2.新建总 Makefile(多个目标时,使用include包含子Makefile)

all : test rapid
include Makefile.maketest
include Makefile.rapidxml

(5)Makefile详解的更多相关文章

  1. Helloworld模块之内核makefile详解

    Hello World 模块以及对应的内核makefile详解 hello.c: #include <linux/module.h> //所有模块都需要的头文件 #include < ...

  2. Makefile详解--隐含规则

    Makefile详解--隐含规则(转) Makefile系列文章,这里有个前辈连续洗了一个系列来介绍,共有26篇博客文章. http://www.cppblog.com/ivenher/archive ...

  3. 【转载】Linux下makefile详解--跟我一起写 Makefile

    概述 —— 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makef ...

  4. linux源码Makefile详解(完整)【转】

    转自:http://www.cnblogs.com/Daniel-G/p/3286614.html 随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 ...

  5. makefile详解 嵌套执行make,定义命令包

    嵌套执行make 在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile,这有利于让我们的Makefile变得更加地简洁,而 ...

  6. linux源码Makefile详解

    1.Makefile的作用 (1)决定编译哪些文件 (2)怎样编译这些文件 (3)怎样连接这些文件,最重要的是它们的顺序如何 2.Linux内核Makefile分类 ***************** ...

  7. 1.Tarball软件make与makefile详解(还需要补充)

    *通常自己安装的软件放在 /usr/local/软件名   中,而将源文件放在/usr/local/src *为安装到单独目录的软件之 man page 加入 man path 搜寻: 如果你安装的软 ...

  8. MakeFile 详解

    最近在学习Linux下的C编程,买了一本叫<Linux环境下的C编程指南>读到makefile就越看越迷糊,可能是我的理解能不行. 于是google到了以下这篇文章.通俗易懂.然后把它贴出 ...

  9. uboot-jiuding 下主Makefile详解

    主Makefile位于uboot源码的根目录下,其内容主要结构为: 1. 确定版本号及主机信息(23至48行)2. 实现静默编译功能(48至55行)3. 设置各种路径(56至123行)4. 设置编译工 ...

  10. linux源码Makefile详解(完整)

    转自:http://www.cnblogs.com/Daniel-G/p/3286614.html 随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 ...

随机推荐

  1. React 现代化测试

    测试的动机 测试用例的书写是一个风险驱动的行为, 每当收到 Bug 报告时, 先写一个单元测试来暴露这个 Bug, 在日后的代码提交中, 若该测试用例是通过的, 开发者就能更为自信地确保程序不会再次出 ...

  2. .NET Core C#中级篇2-5 常见实用类

    .NETCore CSharp 中级篇2-5 本节内容为常见实用类和方法的使用 String.Format string.format方法是一个字符串格式化类,它里面的一些写法是对字符串进行指定格式的 ...

  3. springboot的异步调用

    package com.handsight.platform.fras.aapp; import java.util.Locale; import org.slf4j.Logger; import o ...

  4. Okhttp3源码解析(4)-拦截器与设计模式

    ### 前言 回顾: [Okhttp的基本用法](https://www.jianshu.com/p/8e404d9c160f) [Okhttp3源码解析(1)-OkHttpClient分析](htt ...

  5. c#滑窗缓存

    前言 在大数据时代,软件系统需要具备处理海量数据的能力,同时也更加依赖于系统强大的存储能力与数据响应能力.各种大数据的工具如雨后春笋般孕育而生,这对于系统来说是极大的利好.但在后端采用分布式.云存储和 ...

  6. Spring学习之旅(十五)--SpringBoot

    在使用 Spring 的过程中,有时候会出现一些 ClassNotFoundException 异常,这是因为 JAR 依赖之间的版本不匹配所导致的.而 Spring Boot 就能避免绝大多数依赖版 ...

  7. C# Memcache集群原理、客户端配置详细解析

    概述 memcache是一套开放源的分布式高速缓存系统.由服务端和客户端组成,以守护程序(监听)方式运行于一个或多个服务器中,随时会接收客户端的连接和操作.memcache主要把数据对象缓存到内存中, ...

  8. Unity的UGUI在SetParent后修改UI的localposition问题

    正常情况下,UGUI设置UI的localposition可以直接赋值 UIxxx.rectTransform.localPosition = ] / 2f, , ); 运行后在Unity的Inspec ...

  9. 重温Android和Fragment生命周期

    重温下Android和Fragment生命周期,理解生命周期方法的作用,什么时候调用,可以做一些什么操作. 1.Android生命周期 1.1 生命周期图 1.2 生命周期函数说明 onCreate: ...

  10. spss分析存在共性线后,接下来是怎么分析?

    在进行线性回归分析时,容易出现自变量(解释变量)之间彼此相关,这种情况被称作多重共线性问题. 适度的多重共线性不成问题,但当出现严重共线性问题时,可能导致分析结果不稳定,出现回归系数的符号与实际情况完 ...