zoukankan      html  css  js  c++  java
  • Makefile基础---编译

    首先写一个自己的库:

    [cpp] view plain copy
     
    1. #include "../MyAPI.h"  
    2. #include <cstdlib>  
    3. #include <ctime>  
    4.   
    5. int getRandom(int boundary)  
    6. {  
    7.     if (boundary <= 0 )  
    8.     {  
    9.         return 0;  
    10.     }  
    11.     srand((unsigned)time(NULL));      
    12.     return rand() % boundary;  
    13. }  

    这里的MyAPI.h是库对应的头文件(这里用../MyAPI.h是因为库文件源代码在lib目录下,而头文件跟lib目录在同级目录):

    [cpp] view plain copy
     
    1. int getRandom(int boundary);  

    2. 接着要编译这个库,在这之前需要将源文件编译成.o文件:

    3. 之后再将.o文件打包成lib,在类Unix系统中,静态库是.a文件:

    4. 之后就是使用这个lib库了,下面是使用该库的源代码:

    [cpp] view plain copy
     
    1. #include "basic.h"  
    2.   
    3. int main()  
    4. {  
    5.     cout << getRandom(20) << endl;  
    6.   
    7. }  

    5. 源代码中只需要包含头文件就可以了,重点在于编译的时候,下面是编译命令:

    这里需要注意两点:

    1) -L参数指定包含lib的目录;-l指定lib名;

    2)lib名也需要注意,名称是libMyAPI.a,但是使用时不需要加lib和.a后缀。

    当然也可以不使用-L -l等选项,直接使用lib全称:

    之后生成的a.out就可以使用了。

    这里之所以提到-L和-l参数,原因是在写makefile时,-L和-l会带来更多的便利。

    以上是一个简单的例子。

    也可以写一个makefile文件来做这个,下面是一个makefile的例子:

    [plain] view plain copy
     
    1. #####################################################################  
    2. ## file        : test makefile for build current dir .cpp          ##  
    3. ## author      :                                                   ##  
    4. ## date-time   :                                                   ##  
    5. #####################################################################  
    6.   
    7. CC      = gcc  
    8. CPP     = g++  
    9. RM      = rm -rf  
    10.   
    11. ## debug flag  
    12. DBG_ENABLE   = 1  
    13.   
    14. ## source file path  
    15. SRC_PATH   := .  
    16.   
    17. ## target exec file name  
    18. TARGET     := test  
    19.   
    20. ## get all source files  
    21. SRCS         += $(wildcard $(SRC_PATH)/*.cpp)  
    22.   
    23. ## all .o based on all .c  
    24. OBJS        := $(SRCS:.cpp=.o)  
    25.   
    26.   
    27. ## need libs, add at here  
    28. LIBS := MyApi  
    29.   
    30. ## used headers  file path  
    31. INCLUDE_PATH := .  
    32.   
    33. ## used include librarys file path  
    34. LIBRARY_PATH := lib  
    35.   
    36. ## debug for debug info, when use gdb to debug  
    37. ifeq (1, ${DBG_ENABLE})   
    38. CFLAGS += -D_DEBUG -O0 -g -DDEBUG=1  
    39. endif  
    40.   
    41. ## get all include path  
    42. CFLAGS  += $(foreach dir, $(INCLUDE_PATH), -I$(dir))  
    43.   
    44. ## get all library path  
    45. LDFLAGS += $(foreach lib, $(LIBRARY_PATH), -L$(lib))  
    46.   
    47. ## get all librarys  
    48. LDFLAGS += $(foreach lib, $(LIBS), -l$(lib))  
    49.   
    50. ## c++11 support  
    51. CFLAGS += -std=c++11  
    52.   
    53. all: build  
    54.   
    55. build:  
    56.     $(CPP) -c $(CFLAGS) $(SRCS)  
    57.     $(CPP) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)  
    58.     $(RM) $(OBJS)  
    59.   
    60. clean:  
    61.     $(RM) $(OBJS) $(TARGET)  
    运行make命令之后,make程序会在当前目前下寻找Makefile文件,根据Makefile文件中的内容执行具体的操作。
    执行make命令后的结果如下:
    Makefile的基础是如下的部分:
    [plain] view plain copy
     
    1. targets:prerequisites  
    2.   commands  
    如上例所示:
    a.out是targets,目标;
    main.c是prerequisites,依赖;
    剩余的部分开头是一个TAB,他们是命令;
    make执行命令的条件是依赖比目标要新。
    如果在上例执行后再运行make,结果如下:

    这是因为main.c没有修改过,它比a.out要旧。
    需要注意,目标和依赖都可以是多个,而只要任意依赖新于目标,都会导致命令重新执行。
     
    以上示例中,目标是一个文件,但是它还可以是其它的。比如一个标签:
    all就是一个标签。
    标签永远比依赖要旧,即其下的命令一直会执行:
    甚至可以没有依赖:
    all对应命令也会一直执行。
    标签可以有多个,但是它并不是顺序执行,make只会执行第一个遇到的标签。如下面的Makefile文件:
    执行make的结果如下:
    可以看到只有all标签会被执行。如果要执行bll,就需要显式地指定:
    标签之间可以互相依赖:
    执行的结果如下:
    因为all依赖于bll,而bll又依赖于main.c,因此这里的执行顺序就是先执行bll对应的命令,然后再执行all对应的命令。
     
    还有一种情况,如果在标签之前加上“.”,则make的时候会跳过它,即使它是第一个遇到的标签:
    make执行结果如下:
     
    标签在Makefile文件中又称为伪目标,不应该与真正的目标文件具有相同的名字,而为了避免这种情况,Makefile提供了一个关键字.PHONY,用于声明伪目标。
    像下面这个样子:
     

    变量

    Makefile文件中的变量就是一个个的字符串。
    访问变量使用$(xx),其中xx是变量名。
    下面是一个例子:
    执行的结果是:
    变量的赋值可以使用普通的"=",它有一个特点,如下例所示:
    执行的结果是:
    即,Makefile中使用=定义的变量可以访问到之后定义的变量。使用=得到的变量称为recursively expanded variable。
    它有一个缺点就是会导致循环赋值,比如下面的例子:
    为了避免这种情况,Makefile提供了另一个赋值操作符":=",称为Simply expanded variables。
    它更接近于一般意义上的赋值,当:=右边引用了其它的变量,而该变量还没有定义或者要到之后才会定义,那么返回的就是空字符串。
    另外的赋值操作符还有:
    “+=”:这是给变量追加值。
    "?=":首先判断变量是否存在,如果不存在就赋值成右边的值,如果存在了,就什么也不做。
     
    与变量赋值相关的还有几个指示符:
    override:这个指示符出现的原因是,一般定义的变量会被make命令行中的参数覆盖,而使用了override的变量则不会。下面是一个例子:
    在未使用override的情况下:
    可以看到FLAG参数被make命令行中的参数替代了。
    如果在定义变量的时候使用了override的话:
    变量的值将不会受到make命令行参数的影响。
    export:用于向下层的Makefile文件传递参数。默认情况下,只有在make命令行中的参数才会向下传递,但是使用export之后,在当前Makefile文件中定义的变量也会向下传递了。
     
    Makefile中还存在几类特殊的变量:
    系统环境变量:make执行时会引入部分的环境变量,它们在Makefile中可以访问:
    从结果可以看到Makefile中可以识别HOME这个系统环境变量。
    需要注意的是,并不是所有的系统环境变量都可以在Makefile中使用,比如BASH就不行。具体怎么区分的,不知道。
    目标变量:它是针对某一个目标存在的变量,可以算作是局部变量。它的定义如下:
    [plain] view plain copy
     
    1. targets:xx=yy  
    下面是一个例子:

    从中可以看到FLAG只针对all存在,在bll中无法访问到。
    还需要注意,FLAG不仅只在all中生效,它引发的所有的规则中FLAG都存在。
    模式变量:它其实是目标变量的延伸。其中定义的变量对满足模式的所有目标都可见。下面是一个例子:
    上例中的a.out满足%.out的模式,所以FLAG有效,而对于main.o就无效了。
    自动化变量:它们是一组样式奇怪的变量,也属于局部变量,下面是其中的几个:
    $@ :表示目标集;
    $< :表示依赖中的第一个名字,如果依赖是模式的话,则表示满足模式的所有依赖;
    $? :表示所有比目标新的依赖集;
    $^ :表示依赖集;
    $(@D) :表示$@的目录部分;(还有其它版本的,如$(<D),略)
    $(@F) :表示$@的文件部分;(还有其它版本的,如$(<F),略)
    自动化变量还有很多,不一一介绍了。
    还有一个类型的变量,属于make执行时可以使用的与命令相关的变量,比如如下的:
    执行的结果如下:
    这类变量还有很多,这里不一一说明。
     

    函数

    函数的格式如下:
    [plain] view plain copy
     
    1. $(<fucn><params>)  
    函数名与参数间用空格隔开,参数之间用逗号隔开。
    以下是一个简单的示例:

    make后得到的结果是goodbye world。
    Makefile中的函数将在Makefile基础——函数中详细介绍。
     

    控制语句

    Makefile中的控制语句有以下的一些:
    [plain] view plain copy
     
    1. ifeq/ifneq  
    2. else  
    3. endif  
    举例:
    [plain] view plain copy
     
    1. ifdef/ifndef  
    2. else  
    3. endif  
    举例:

    函数的基础请参考Makefile基础

    字符串处理函数

    [plain] view plain copy
     
    1. $(subst <from>,<to>,<text>)  

    作用:将text中的from替换成to,并返回修改后的text(当然也有可能没有被修改)。

    [plain] view plain copy
     
    1. $(patsubst <pattern>,<replacement>,<text>)  
    作用:基本同subst,只是这里变成了对满足模式的字符串的替换。
    [plain] view plain copy
     
    1. $(strip <string>)  
    作用:将string前后的空格去掉并返回修改后的string(当然也有可能没有被修改)。
    [plain] view plain copy
     
    1. $(findstring <find>,<in>)  
    作用:在in中找字符串find,如果找到了就返回find,否则返回空。
    [plain] view plain copy
     
    1. $(filter <pattern...>,<text>)  
    作用:过滤text字符串,只保留符合模式的字符串,pattern可以有多个。
    [plain] view plain copy
     
    1. $(filter-out <pattern...>,<text>)  
    作用:同filter刚好相反,这里删除符合模式的字符串。
    [plain] view plain copy
     
    1. $(sort <list>)  
    作用:升序排序list里面的单词。
    [plain] view plain copy
     
    1. $(word <n>,<text>)  
    作用:取text中的第n个单词,从1开始算。
    [plain] view plain copy
     
    1. $(wordlist <s>,<e>,<text>)  
    作用:取text中第s个开始到第e个为止的单词,包括第e个单词。
    [plain] view plain copy
     
    1. $(words <text>)  
    作用:返回text中的单词数。


    [plain] view plain copy
     
    1. $(firstword <text>)  
    作用:返回text中的第一个单词。


    文件名操作

    [plain] view plain copy
     
    1. $(dir <names...>)  
    作用:取names中的目录部分。
    [plain] view plain copy
     
    1. $(nodir <names...>)  
    作用:取names中的非目录部分。
    [plain] view plain copy
     
    1. $(suffix <names...>)  
    作用:取names中的后缀部分,比如main.c就取得.c部分。
    [plain] view plain copy
     
    1. $(basename <names...>)  
    作用:取names中的前面部分,比如main.c就取得main部分。
    [plain] view plain copy
     
    1. $(addsuffix <suffix>,<names...>)  
    作用:给names中的每一个成员加后缀suffix。
    [plain] view plain copy
     
    1. $(addprefix <prefix>,<names...>)  
    作用:给names中的每一个成员加前缀prefix。
    [plain] view plain copy
     
    1. $(join <list1>,<list2>)  
    作用:将list1中的单词加到list2对应位置的单词之后。没有对应的就不管了。
    [plain] view plain copy
     
    1. $(abspath <names...>)  
    作用:返回绝对路径。

    结构函数

    [plain] view plain copy
     
    1. $(foreach <var>,<list>,<text>)  
    作用:取出list中的每一个单词并赋值给var,再执行text语句。
    [plain] view plain copy
     
    1. $(if <condition>,<then-part>,<else-part>)  
    作用:判断condition条件,满足条件就执行then-part,否则执行else-part。两个part都可以是空。

    call函数

    [plain] view plain copy
     
    1. $(call <expression>,<param1>,<param2>...)  
    作用:执行expression,后面的是参数,它们在expression中用$(1)$(2)...表示。

    origin函数

    [plain] view plain copy
     
    1. $(origin <var>)  
    作用:确定var的来源,是undefined?default?environment?file?command line?override?automatic?

    shell函数

    [plain] view plain copy
     
    1. $(shell <cmd>)  

    作用:执行cmd命令。相当于`cmd`。

    eval函数

    [plain] view plain copy
     
    1. $(eval <text>)  
    作用:不明。

    似乎是展开text,作为Makefile的一部分。但是为什么要这么做,为什么不直接写text,不明白。。。

    告警/错误函数

    [plain] view plain copy
     
    1. $(error <tetx>)  
    作用:显示错误并停止执行。



    [plain] view plain copy
     
    1. $(warning <text>)  
    作用:显示告警,但是会继续执行。

    wildcard函数

    [plain] view plain copy
     
    1. $(wildcard pattern...)  
    作用:返回工作目录下满足pattern的所有文件名,多个文件名之间用空格隔开。

    运行make之后的结果:

  • 相关阅读:
    第二十章 数据访问(In .net4.5) 之 使用LINQ
    第十九章 数据访问(In .net4.5) 之 处理数据
    第十八章 数据访问(In .net4.5) 之 I/O操作
    第十七章 调试及安全性(In .net4.5) 之 程序诊断
    大叔学Spring Boot笔记(14)A component required a bean of type '...Mapper' that could not be found问题解决
    大叔学Spring Boot笔记(13)Free Mybatis plugin使用
    MySQL查询结果中Duration Time和Fetch Time的区别
    大叔学Spring Boot笔记(12)Windows环境下使用bat启动和停止Java【转】
    大叔学Spring Boot笔记(11)jdk/bin目录下的不同exe文件的用途及区别【转】
    大叔学Spring Boot笔记(十)手动编译、打包并运行项目
  • 原文地址:https://www.cnblogs.com/klb561/p/8921942.html
Copyright © 2011-2022 走看看