首页 > 代码库 > Linux 程序设计学习笔记----动手编写makefile文件

Linux 程序设计学习笔记----动手编写makefile文件

Befroe Beginning.

之前定了暑假的plan ,关于Linux的书籍现在在看的是ALP和Linux高级程序设计(杨宗德)第三版.在计划中的是Linux高级环境编程.

现在开始关于Linux程序设计的第一篇学习笔记.

本来打算把名字写成教程,不过觉得自己完全是新手在自学,还是写学习笔记比较负责和适合.

希望可以一起学习进步.

引入

首先我们假设这样一个场景.我们有一个程序包含了三个文件,分别是源码文件main_plus,c和function_plus.c以及头文件mydefine_plus.h.

其中main主要是调用功能函数,功能函数则是实现简单的累加,头文件声明函数和一些库函数.代码分别如下:

main_plus.c

/*************************************************************************
    > File Name: main_plus.c
    > Author: suool
    > Mail: 1020935219@qq.com 
    > Created Time: 2014年07月23日 星期三 17时31分23秒
************************************************************************/

/**@file main_plus.c */
/** 接收参数,调用功能函数,输出结果. */

#include "mydefine_plus.h"

int main(void)
{
    int a=0, b=0;
    printf("这是一个求和的程序,请输入数字a和b,程序讲输出a到b的累加结果.\n");
    printf("Please enter integer a:");
    scanf("%d", &a);
    printf("\nPlease enter integer b:");
    scanf("%d", &b);
    if(a>b)
    {
        printf("\nThe sum is %d\n", plus(b,a));
    }
    else
    {
        printf("\nThe sum is %d\n", plus(a,b));
    }
    return 0;
}
function_plus.c
/*************************************************************************
	> File Name: function_plus.c
	> Author: suool
	> Mail: 1020935219@qq.com 
	> Created Time: 2014年07月23日 星期三 17时31分40秒
 ************************************************************************/
/**@file function_plus.c */
/** 对a到b的累加求和*/

#include "mydefine_plus.h"

int plus(int a, int b)
{
    int sum = a;
    int i;
    for (i=a+1; i<=b; i++)
    {
        sum += i;
    }
    return sum;
}
mydefine_plus.h.
/*************************************************************************
> File Name: mydefine_plus.h
> Author: suool
> Mail: 1020935219@qq.com 
> Created Time: 2014年07月23日 星期三 17时36分16秒
************************************************************************/
/** @file mydefine_plus.h */
/** 函数声明和包含*/

#ifndef _MYDEFINE_PLUS_H
#define _MYDEFINE_PLUS_H
#include <stdio.h>
int plus(int a, int b);
#endif

现在我们要编译这个程序,我们可以怎么做呢?

这就是这次要解决的问题.

make文件编写

对于上面的问题,我们传统的解决方法是这样的:

即是分别编译这两个文件,然后链接变成目标可执行文件,

当然,对于三个甚至五个的这样的程序都是可以的,但是如果对于更大的程序呢?

或者我们修改了某个程序,难道要重新编写这些命令?

显然,这不是一个明智的选择,因此,我们便导出make文件,即自动执行编译的文件.只要执行一下make命令,everything is done  !

so, let`s see how to do it.

首先我们先把这个程序的文件的makefile文件贴一下,如下:

makefile

main : main_plus.o function_plus.o
	gcc -o main main_plus.o function_plus.o
main_plus.o : main_plus.c mydefine_plus.h
	gcc -c main_plus.c
function_plus.o : function_plus.c mydefine_plus.h
	gcc -c function_plus.c
clean:
	rm -f *.o main
makefile文件的注释是#后面的语句.

有Makefile文件后,不管我们什么时候修改了源程序当中的什么文件,我们只要执行make命令,我们的编译器都只会去编译与我们修改的文件有关的文件,其它的文件不会处理.

验证如下:先执行一次make命令,如下:

这次自动编译了两个c文件,我们现在修改function_plus.c文件,再次执行make命令,结果如下:

这次只是对刚刚修改的文件进行了编译.


makefile文件编写规则

观察其结构我们可以得出make文件的编写基本规则:

Makefile文件中,注释以"#"开始

Makefile文件中最重要的是描述文件的依赖关系的说明,其一般的格式为:

target : components

TAB rule

即是这样:

目标 : 需要的条件 (注意冒号两边有空格)

    命令  (注意前面用tab键开头

解释一下:

  1 目标可以是一个或多个,可以是Object File,也可以是执行文件,甚至可以是一个标签。

  2 需要的条件就是生成目标所需要的文件或目标

  3 命令就是生成目标所需要执行的脚本

  总结一下,就是说一条makefile规则规定了编译的依赖关系,也就是目标文件依赖于条件,生成规则用命令来描述。在编译时,如果需要的条件的文件比目标更新的话,就会执行生成命令来更新目标。

需要注意的是上面的claen命令,clean后面没有条件,而clean本身也不是文件,它只不过是一个动作名字,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。

例如:上面的makefile文件的第一行,生成的最终的文件为main,他所依赖的是两个.o文件,而这两个文件依赖于源文件的编译.

Makefile的常用变量

Makefile 有三个非常有用的变量:$@,$^,$<。其意义为:

$@:目标文件

$^:所有的依赖文件

$<:第一个依赖文件

则上面的可简化为:

#这是简化后的Makefile
main : main_plus.o function_plus.o
	gcc -o $@ $^
main_plus.o : main_plus.c mydefine_plus.h
	gcc -c $<
function_plus.o : function_plus.c mydefine_plus.h
	gcc -c $<
clean:
	rm -f *.o main

make如何工作

在默认的方式下,也就是我们只输入make命令.那么,

    1.make会在当前目录下找名字叫“Makefile”或“makefile”的文件
    2.如果找到.它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“main”这个文件,并把这个文件作为最终的目标文件
    3.如果main文件不存在,或是main所依赖的后面的 .o 文件的文件修改时间要比main这个文件新,那么,他就会执行后面所定义的命令来生成main这个文件
    4.如果main所依赖的.o文件也不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
    5.当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件main了

makefile中使用变量

首先看下:

main : main_plus.o function_plus.o
	gcc -o $@ $^
这里的依赖项有两个,但是如果有很多个呢?那个时候我们需要增加新的依赖相,要在这里添加,如果没有使用符号,那么还需要在下面的命令下添加,因此,很容易遗漏.But 还好,makefile可以定义变量,这样:

object = main_plus.o function_plus.o
main : $(object)
	gcc -o $@ $(object)

让make自动推导

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且 gcc -c whatever.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。

最新的makefile:

#这是简化后的Makefile
main : main_plus.o function_plus.o
	gcc -o $@ $^
main_plus.o : mydefine_plus.h
function_plus.o : mydefine_plus.h
clean:
	rm -f *.o main

清空目标文件的规则

每个Makefile中都应该写一个清空目标文件(.o和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。一般的风格都是:

        clean:
            rm edit $(objects) $@

更为稳健的做法是:

        .PHONY : clean
        clean :
                -rm edit $(objects)

前面说过,.PHONY意思表示clean是一个“伪目标”,。而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean从来都是放在文件的最后”。


so,大概的内容就是这样,接下来我要写一下关于C语言的东西了,因为既然是Linux程序设计,当然重点还是在语言和代码上面,而不是OS本身.

接下来的内容:

1.ANSI C文件I/O管理

2.POSIX文件以及目录管理

3.Linux下的一些编码特性和规范