学习写makefile

来源:互联网 发布:ambari web 源码编译 编辑:程序博客网 时间:2024/06/04 00:51

学习写makefile



一、本文的起因:

    刚毕业到公司之后,系统中都是linux c++程序,开始编写测试程序的时候,只会用命令g++ -g -O -o test test.cpp进行编写,每次打字都很累。。。可怜



上网查了一下,可以直接编写makefile文件,然后直接make即可。于是自己尝试去写,结果好几次都失败了:下一行命令应该用tab键,结果打了好多空格,报了很多错误。尴尬

接着又是没有耐心去看繁琐的规则和格式,总也写不好一个完整的makefile。


于是,就放弃写makefile了。直接用公司写好的makefile模板,简单、方便!得意


最近,觉得自己要积累点知识了,而不是随着项目内容飘来飘去。


所以,本文理论部分基本上是网上内容的总结。加入了自己的理解。如有错误,敬请指出! 谢谢!得意

涉及到的来源:

http://blog.csdn.net/ruglcc/article/details/7814546/


二、实际的例子

我们先从makefile基本语法开始,再给出自己写makefile的例子,进行逐步深入讲解:


  1. makefile基本语法

target ... : prerequisites ...   #可以有多目标
  command 

target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

prerequisites就是,要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。(任意的Shell命令)


2.  逐步学习的例子

   看过了makefile基本语法的例子,那么就勇敢地着手写一个吧!因为编写好了makefile后,只需make,即可完成整个编译过程。后续再增加或删除源代码文件,也只需简单修改makefile文件即可。


源代码如下:

main.cpp
#include "hello.h"#include "cal.h"#include <iostream>using namespace std;int main(int argc, char *argv[]){    cout << "begin to print hello-------------------" << endl;    print_hello();    print_hello(3);    int a = 3;    int b = 4;    int retcode = 0;        cout << "begin to calculate-------------------" << endl;    retcode = cal_minus(a,b);    retcode = cal_plus(a,b);    return 0;}
hello.h
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int print_hello();int print_hello(int times);
hello.cpp
#include "hello.h"#include <iostream>using namespace std;int print_hello(){<span style="white-space:pre"></span>cout << "this is Hello Function!" << endl;        return 0;}int print_hello(int times){        if (times <= 0){<span style="white-space:pre"></span>return -1;}<span style="white-space:pre"></span>for (int i = 0; i < times; i++)<span style="white-space:pre"></span>{<span style="white-space:pre"></span>cout << "this is [" << i << "] hello!" << endl; <span style="white-space:pre"></span>}         return 0;}
cal.h
#include <stdio.h>#include <unistd.h>#include <stdlib.h>int cal_plus(int a, int b);int cal_minus(int a, int b);
cal.cpp
#include "cal.h"#include <iostream>using namespace std;int cal_plus(int a, int b){    cout << a << "+" << b << "=" << a+b << endl;    return (a+b);}int cal_minus(int a, int b){    cout << a << "-" << b << "=" << a-b << endl;    return (a-b);}

从代码中可以看到:main.cpp引用了hello.h和cal.h两个头文件,为了使用它们中定义的函数。


2.1 第一版makefile如下:

cc=g++ccflag=-Wall -fpicmain:main.o hello.o cal.o$(cc) -o main main.o hello.o cal.omain.o:main.cpp$(cc) -c  main.cppcal.o:cal.cpp cal.h$(cc) -c  cal.cpphello.o:hello.cpp hello.h$(cc) -c  hello.cppclean:rm *.orm main
本文件中清晰地写好了:

   1)目标文件main依赖哪些中间文件,main是如何生成的(通过$(cc) -o main main.o hello.o cal.o)

   2)中间文件main.o cal.o hello.o依赖哪些源文件和头文件,并通过什么命令生成的。

   3)最后,clean是个为目标,通过make clean命令调用 rm *.o rm main



2.2 优化2.1中的makefile

    下面是优化的makefile:

cc=g++ccflag=-Wall -fpicmain:main.o hello.o cal.o$(cc) -o $@ $^main.o:main.cpp$(cc) -c  main.cppcal.o:cal.cpp cal.h$(cc) -c  cal.cpphello.o:hello.cpp hello.h$(cc) -c  hello.cppclean:rm *.orm main




2.3 优化2.2中的makefile

    下面是优化的makefile:

main:main.o hello.o cal.og++ -o $@ $^%.o:%.cppg++ -c  $^clean:rm *.orm main
  




以下为详细而全面的makefile编写方法:


先说一下编译和链接,这是我们写makefile的最终目的:

0.1 关于程序的编译和链接

   在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是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错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的ObjectFile.


       补充一下自己的经验:gcc和g++编译器都可以进行中间文件的编译,同时也可以链接中间文件形成目标文件。所以,在makefile中,无论是编译中间文件,还是生成目标文件的编译,都是使用gcc或者g++来完成的(When you invoke GCC, it normally does preprocessing, compilation, assembly and linking.)。其中gcc主要编译c程序,g++主要编译g++文件。

                                             还有,需要了解gcc和g++如何使用,也是makefile中重要的一部分。


0 0
原创粉丝点击