1、编写一个简单Makefile模板

一、Makefile简介

一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。linux内核的编译同样也遵循这些规则,具体说明可见kernel/Documentation/kbuild/makefiles.txt

二、简单编写一个Makefile模板

当编译少量的源文件时,可以用以下两种方法编译

1、直接用一条命令编译

  gcc -o test 1.c 2.c

编译过程说明:

执行gcc -o test 1.c 2.c时,

对于1.c:预编译、编译、汇编、链接

对于1.c:预编译、编译、汇编、链接

优点:命令简单,易操作

缺点:如果文件很多,即使修改了一个文件,但是所有的文件都要重新"预处理、编译、汇编",效率很低

2、通过Makefile建立编译规则

一个的Makefile文件包含一系列的规则(想了解更多规则可参考《GNU make手册》),其格式如下:

目标 : 依赖1 依赖2 ... 依赖n

  命令

注意:命令的前边必须是一个Tab

命令执行的条件:

(1)"依赖"文件比"目标"的时间新

(2)没有"目标"这个文件

三、代码实践

功能说明:当任何一个文件发生改变时,其所有依赖文件和自己都会被重新编译,而其他与此没有关联的文件则不会被编译

源文件如下:

<1>文件1.c

#include <stdio.h>
#include "1.h"
int main(char argc, char *argv[])
{
    printf("This is 1.c\n");
    func();
    printf("AA = %d\n", AA);

    return 0;
}

<2>文件2.c

#include <stdio.h>
void func(void)
{
    printf("This is 2.c\n");
}

<3>文件1.h

#define AA     2

1、第1个Makefile

1 target:1.c 2.c 1.h
2     gcc -o target 1.c 2.c

解析:
第一次编译(没有目标生成)或依赖发生变化(依赖比目标新)就执行下面的命令这样的缺点就是,只要其中的一个依赖发生变化,所有的文件都要重新被编译一遍,这样效率极低

2、第2个Makefile

target:1.o 2.o
    gcc -o target 1.o 2.o
#-c表示只编译不链接(只进行预编译、编译、汇编)
1.o : 1.c
    gcc -c -o 1.o 1.c

2.o : 2.c
    gcc -c -o 2.o 2.c

解析:

解析:当执行make命令时,想要生成第一个目标target,而target依赖于1.o、2.o,但是当前目录下并没有1.o、2.o,它会用1.o往下查找到1.o依赖于1.c,同时判断(1.c是否比1.o新或1.o是否存在)是否执行gcc -c -o .1.o 1.c,我们这里满足第二个条件所以此命令会被执行,分析1.o也是如此

3、第3个Makefile(效果同第2个Makefile)

target:1.o 2.o
    gcc -o target 1.o 2.o

%.o : %.c
    gcc -c -o [email protected] $<

解析:

%.o : %.c,
gcc -c -o [email protected] $<
等同于
1.o : 1.c
gcc -c -o 1.o 1.c
2.o : 2.c
gcc -c -o 2.o 2.c
%为通配符,%.o,表示所有的.o文件%.c,表示所有的.c文件,
[email protected]表示目标(对应%.o),$<表示第一个依赖(对应%.c)

测试:
(1)第一次编译,则所有的文件都会被编译
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# ls
1.c 1.h 2.c Makefile
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# make
gcc -c -o 1.o 1.c
gcc -c -o 2.o 2.c
gcc -o target 1.o 2.o
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# ls
1.c 1.h 1.o 2.c 2.o Makefile target

(2)只修改1.c,则所有依赖1.c的文件都会重新被编译,而2.c不会重新被编译
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# vi 1.c
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# make
gcc -c -o 1.o 1.c
gcc -o target 1.o 2.o

(3)只修改2.c,则所有依赖2.c的文件都会重新被编译,而1.c不会重新被编译
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# vi 2.c
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# make
gcc -c -o 2.o 2.c
gcc -o target 1.o 2.o

(4)但是修改1.h就不行了,不能检测到1.h的更新,第4个Makefile中继续修改
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# vi 1.h
[email protected]:/mnt/hgfs/winshare/mak/2_makefile# make
make: `target‘ is up to date.

4、第4个Makefile

target:1.o 2.o
    gcc -o target 1.o 2.o
#添加此行,当修改1.h时,所有依赖1.h的文件都会重新编译
1.o : 1.c 1.h

%.o : %.c
    gcc -c -o [email protected] $<

解析:
(1)1.o依赖于1.c,同样也依赖于1.h,故添加1.o : 1.c 1.h
(2)make时就会检测1.c所包含的1.h是否发生改变,若发生改变,则重新编译
(3)但存在一个问题,当有上万个.c文件时,要查看它所包含的.h文件是否发生改变,这样操作就不行了,那么是否有什么自动的规则,来生成依赖文件,第5个Makefile中继续修改

测试:
[email protected]:/mnt/hgfs/winshare/mak/4_makefile# make
gcc -c -o 1.o 1.c
gcc -o target 1.o 2.o

5、第5个Makefile

利用-Wp,-MD,1.d自动生成依赖存到1.d中,1.o的所有依赖如下
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# gcc -c -o 1.o 1.c -Wp,-MD,1.d
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# ls
1.c 1.d 1.h 1.o 2.c Makefile
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# vi 1.d
1.o: 1.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/features.h /usr/include/i386-linux-gnu/sys/cdefs.h \
/usr/include/i386-linux-gnu/bits/wordsize.h \
/usr/include/i386-linux-gnu/gnu/stubs.h \
/usr/include/i386-linux-gnu/gnu/stubs-32.h \
/usr/lib/gcc/i686-linux-gnu/4.8/include/stddef.h \
/usr/include/i386-linux-gnu/bits/types.h \
/usr/include/i386-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/i686-linux-gnu/4.8/include/stdarg.h \
/usr/include/i386-linux-gnu/bits/stdio_lim.h \
/usr/include/i386-linux-gnu/bits/sys_errlist.h 1.h

target:1.o 2.o
    gcc -o target 1.o 2.o

%.o : %.c
    gcc -c -Wp,-MD,[email protected] -o [email protected] $<

clean:
    rm *.o target

distclean:
    rm *.o target *.d

(1)-Wp,-MD,[email protected]表示编译时自动生成1.o.d、2.o.d,所有关于1.o、2.o的依赖
都保存在1.o.d、2.o.d
(2)clean,distclean表示伪目标,make clean 时,则执行rm *.o target,
make distclean也如此

6、第6个Makefile

objs := 1.o 2.o

target:$(objs)
    gcc -o target $^
$(warning $(objs))
# .1.o.d .2.o.d
dep_files := $(foreach f,$(objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
$(warning $(dep_files))
ifneq ($(dep_files),)
    include $(dep_files)
endif

%.o : %.c
    gcc -c -Wp,-MD,[email protected] -o [email protected] $<

clean:
    rm *.o target

distclean:
    rm -f  *.o target .*.d

解析:
(1)定义一个立即变量objs来表示target的依赖
(2)$(foreach f,$(objs),.$(f).d)的分析:
foreach  含义是每一个,是一个函数,类似于for循环
     f   代表$(objs)中的一个成员变量1.o或2.o,
.$(f).d    把$(objs)中的每个成员都加上前缀.和后缀.d,如.1.o.d和.2.o.d
(3)$(wildcard $(dep_files))表示取出$(dep_files)中的成员变量
(4)ifneq ($(dep_files),)判断$(dep_files)是否非空,非空条件成立
(5)至此,所有.h类型的依赖被包含进去

测试:
单独修改1.h编译成功,修改AA的值为3
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# vi 1.h
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# make
.1.o.d .2.o.d
gcc -c -Wp,-MD,.1.o.d -o 1.o 1.c
gcc -o target 1.o 2.o
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# la
1.c 1.h 1.o .1.o.d 2.c 2.o .2.o.d Makefile target
[email protected]:/mnt/hgfs/winshare/mak/6_makefile# ./target
This is 1.c
This is 2.c
AA = 3

原文地址:https://www.cnblogs.com/sbtblogs/p/10231892.html

时间: 2024-08-08 10:04:23

1、编写一个简单Makefile模板的相关文章

编写一个简单的js模板替换工具 rtt----replace templete tool

最近一段时间在修改自己的个人在线简历.  这个在线简历用到了css3来制作3D的旋转效果, 因此会有兼容性问题, 针对于不支持css3的3D透视的浏览器, 比如 IE,  360等等, 我使用的是另一套css文件兼容.  针对于移动端浏览器, 尽管基本都是webkit内核, 但经测试发现3D效果并不流畅, 因此移动端是识别userAgent切换到另一套非3D页面.  因为没用任何数据库, 那么问题就来了, 移动端和pc端两套页面是共用的一套数据, 我想到的方法有两个: 一是页面加载之后用ajax

编写一个简单的内核驱动模块时报错 “/lib/modules/3.13.0-32-generic/bulid: 没有那个文件或目录。 停止。”

编写一个简单的内核驱动模块 1 static int hello_init() 2 { 3 printk("hello,I am in kernel now\n"); 4 return 0; 5 } 6 void addfunc(int a,int b) 7 {return a+b;} 8 static void hello_exit() 9 { 10 printk("hello ,I will leave the kernel now\n"); 11 } 12 m

【C++】编写一个简单的类。包含构造函数,成员函数等。

<pre name="code" class="cpp">//编写一个简单的类.包含构造函数,成员函数等. #include <iostream> using namespace std; class Rec { public: Rec(int l,int w); int Area(); void Print(); private: int length,wide; }; Rec::Rec(int l,int w) { length=l; w

【C++】编写一个简单的函数实现重载。

//编写一个简单的函数实现重载. #include <iostream> using namespace std; int max(int a,int b) { return a>b?a:b; } int max(int a,int b,int c) { int x=max(a,b); return max(x,c); } double max(double a,double b) { return a>b?a:b; } int main() { cout<<"

手把手教你编写一个简单的PHP模块形态的后门

看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一个非常流行的web server端的script语言.目前很多web应用程序都基于php语言实现.由于php是个开源软件并易于扩展,所以我们可以通过编写一个PHP模块(module 或者叫扩展 extension)来实现一个Backdoor. 本文就简单介下如何一步步编写一个简单的php 动态扩展后门. 0×01. p

编写一个简单的jdbc例子程序

1 package it.cast.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 public class Base { 10 11 public static void main(String[] args) th

编写一个简单的javaEE加法程序

一 .javaEE的安装及环境配置 工具: 32位系统准备eclipse-jee-mars-2-win32.zip,64位系统准备eclipse-jee-mars-2-win32-x86_64.zip jdk1.7 maven3.3.9.rar m2.rar 环境配置: 1. 设置eclipse的配置文件eclipse.ini,修改虚拟机路径,在-vmargs之前添加 -vm E:\jee\jdk1.7\bin\javaw.exe 注意:用写字板打开修改,-vm有的电脑要换行,有的电脑不用换行

用 C 语言编写一个简单的垃圾回收器

人们似乎认为编写垃圾回收机制是很难的,是一种只有少数智者和Hans Boehm(et al)才能理解的高深魔法.我认为编写垃圾回收最难的地方就是内存分配,这和阅读K&R所写的malloc样例难度是相当的. 在开始之前有一些重要的事情需要说明一下:第一,我们所写的代码是基于Linux Kernel的,注意是Linux Kernel而不是GNU/Linux.第二,我们的代码是32bit的.第三,请不要直接使用这些代码.我并不保证这些代码完全正确,可能其中有一些我 还未发现的小的bug,但是整体思路仍

Swift语言编写一个简单的条形码扫描APP

swift语言编写一个简单的条形码扫描APP 原文地址:appcoda 在处理职员在杂货店的收银台排了很长的队伍,在机场帮助检查背包和旅客,或者在主要的食品供应商,协助处理乏味的存货清单过程,条形码扫描是很简单的处理工具.实际上,他们已经用了这个办法来解决消费者在智能购物,图书分类,等其他目的.因此,让我们来制作一个iPhone版本的条形码扫描工具吧! 对我们来说幸运的是,苹果已经制作了条形码扫描的程序,实现它是一件很简单的事情.我们将要研究进入AV Foundation框架的世界,组建APP,