Makefile实际操作详解

一、makefile 单个C文件

        

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。

1.复习gcc指令
    一个非常简单的C文件——test.c
    【test.c】
  1. #include <stdio.h>  
  2. int main(void)  
  3. {  
  4.     int a = 3;  
  5.     int b = 2;  
  6.           
  7.     printf("a=%d\n",a);  
  8.     printf("b=%d\n",b);  
  9.     return 0;  
  10. }  
    【最简单方法】
    gcc test.c -o test
    最终生成可执行文件 test
    【执行test】
    ./test
    【输出结果】
    a=3
    b=5
    
    【不正确的写法】
    请注意以下写法并不正确。
    gcc -c test.c -o test

    【详细步骤分解】编译——链接
    无论gcc指令的参数如何变化,从源文件变为可执行文件只需要两步,第一步源文件 编译为目标文件,第二步从目标文件 链接为可执行文件。在最简单的指令——gcc test.c -o test中使用了一处GCC的隐含规则,所有编译和链接这两个关键步骤展现的不明显。那么下面通过指令让“不明显”变得“明显”。
【1】由c文件编译为目标文件
    【写法1】
    gcc -c test.c -o test.o
    【写法2】——顺序可以颠倒
    gcc -o test.o -c test.c
    【写法3】——适当简写
    可以适当简写,充分利用GCC的默认规则,*.o文件由同名的*.c文件编译得到。
    gcc -c test.c
    【写法4】——有点奇怪
    还可以这样写,虽然看起来有点奇怪,但只执行结果却是一样的。makefile文件似乎更喜欢这种方式。
    gcc -c -o test.o test.c

【2】把目标文件链接为可执行文件
    【写法1】
    gcc test.o -o test
    【写法2】——顺序可以颠倒
    顺序可以颠倒,makefile文件似乎更喜欢这种方式。
    gcc -o test test.o

2.编写makefile文件
    【makefile】
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 可执行文件  
  2. TARGET = test  
  3. # 依赖目标  
  4. OBJS = test.o  
  5.   
  6. # 指令编译器和选项  
  7. CC=gcc  
  8. CFLAGS=-Wall -std=gnu99  
  9.   
  10. $(TARGET):$(OBJS)  
  11. # @echo TARGET:$@  
  12. # @echo OBJECTS:$^  
  13.  [tab]$(CC) -o $@ $^  
  14.   
  15. clean:  
  16.  [tab]rm -rf $(TARGET) $(OBJS)  

    【具体说明】
    【1】TARGET=test test为最后可执行文件,linux中的可执行文件就是windows中的exe文件
    【2】OBJS = test.o test.o对应test.c,利用makefile的隐含规则,test.o由test.c编译得到。
    【3】CC=gcc 指定编译器为gcc
    【4】CFLAGS=-Wall -std=gnu99 使能所有警告,指定编译器标准为gnu99
    【5】 $(CC) -o $@ $^ 
            $@和$^为自动化变量,$@指目标文件,此处为可执行文件test,$^指去除重复的依赖文件,此处为test.o
            $(CC) -o $@ $^ 最终变化为 gcc -o test test.o。gcc -o test test.o和【详细步骤】链接部分的指令完全相同。那么makefile和gcc指令便建立了联系。
            可以通过@echo指令在makefile执行过程中打印自动化变量,通过这种方式调试makefile加速错误修正。

    【编译】
    make clean && make
    先执行make clean再执行make生成可执行文件

    【控制台输出】
gcc -Wall -std=gnu99 -c -o test.o test.c
gcc -o test test.o

    【分析】
    若去除-Wall -std=gnu99,那么以上两句简化为
gcc -c -o test.o test.c 和编译过程方法【4】相同
gcc -o test test.o       和执行过程方法【2】相同  
    那么makefile和gcc指令便建立了关系,理解起来也方便多了。          

3.再完善一些
    【makefile】
    请替换其中的[tab],并以代码仓库中的makefile文件为主
  1. # 可执行文件  
  2. TARGET=test  
  3. # C文件  
  4. SRCS = test.c  
  5. # 目标文件  
  6. OBJS = $(SRCS:.c=.o)  
  7.   
  8. # 指令编译器和选项  
  9. CC=gcc  
  10. CFLAGS=-Wall -std=gnu99  
  11.   
  12. $(TARGET):$(OBJS)  
  13. #   @echo TARGET:$@  
  14. #   @echo OBJECTS:$^  
  15.  [tab]$(CC) -o $@ $^  
  16.   
  17. clean:  
  18.  [tab]rm -rf $(TARGET) $(OBJS)  
  19.   
  20. %.o:%.c  
  21.  [tab]$(CC) $(CFLAGS) -o $@ -c $<  
    【变化说明】
    【1】OBJS = $(SRCS:.c=.o) 变量替换函数,把所有的*.c文件替换为*.o文件。该函数和
            OBJS = $(patsubst %.c,%.o,$(SRCS))具有相同效果。
    【2】$<为自动化变量,指第一个目标文件,此处为test.c
            替换变量和自动化变量之后:
            test.o:test.c
            [tab]gcc -Wall -std=gnu99 -o test.o -c test.c

4.总结
    【1】gcc指令执行顺序——先编译目标文件,后链接成可执行文件
    【2】自动化变量
            $@ 当前规则的目标文件
            $< 第一个依赖文件

            $^ 去除重复的所有依赖文件

二、例说makefile 多个文件

        

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。

1.三个C文件
    三个文件位于同一个目录中。
    【test.c】
  1. #include <stdio.h>  
  2. int main(void)  
  3. {  
  4.     int a = 3;  
  5.     int b = 2;  
  6.           
  7.     printf("a=%d\n", a);  
  8.     printf("b=%d\n", b);  
  9.       
  10.     printf("a+b=%d\n", add(a,b));  
  11.     printf("a-b=%d\n", sub(a,b));  
  12.     return 0;  
  13. }  
    
    【test-add.c】
  1. int add(int a, int b)   
  2. {  
  3.     return a+b;  
  4. }  
    
    【test-sub.c】
  1. int sub(int a, int b)   
  2. {  
  3.     return a-b;  
  4. }  

2.复习GCC指令
    多个C文件和单个C文件的处理过程相似,此时-c或-o之后可以增加多个C文件或目标文件。
    【编译源文件】
    gcc -c  test.c test-add.c test-sub.c
    【链接目标文件】
    gcc -o test  test.o test-add.o test-sub.o
    【执行目标文件】
    ./test
    【控制台输出】
    a=3
    b=2
    a+b=5
    a-b=1

3.编写makefile文件
    【makefile】
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 指令编译器和选项  
  2. CC=gcc  
  3. CFLAGS=-Wall -std=gnu99  
  4.   
  5. # 目标文件  
  6. TARGET=test  
  7. # 源文件  
  8. SRCS=test.c test-add.c test-sub.c  
  9.   
  10. OBJS = $(SRCS:.c=.o)  
  11.   
  12. $(TARGET):$(OBJS)  
  13. #   @echo TARGET:$@  
  14. #   @echo OBJECTS:$^  
  15. [tab]$(CC) -o $@ $^  
  16.   
  17. clean:  
  18. [tab]rm -rf $(TARGET) $(OBJS)  
  19.   
  20. %.o:%.c  
  21. [tab]$(CC) $(CFLAGS) -o $@ -c $<  

    【具体说明】
    【1】更多说明请参考:例说makefile——单个文件
    【2】相对于单个文件,此处仅修改了SRCS=test.c test-add.c test-sub.c,变量SRCS追加了多个C文件。相比单个文件的情况,仅仅修改了这一行。
    【3】文件和文件之间通过空格隔开。
    【执行过程】
    make clean && make 
    【控制台输出】
rm -rf test test.o test-add.o test-sub.o
gcc -Wall -std=gnu99 -o test.o -c test.c
gcc -Wall -std=gnu99 -o test-add.o -c test-add.c
gcc -Wall -std=gnu99 -o test-sub.o -c test-sub.c
gcc -o test test.o test-add.o test-sub.o

    从控制台的输出可以看出,通过make clean清除上一次的可执行文件和目标文件,然后依次编译各个C文件,最后把3个目标文件链接为最终可执行文件。

三、例说makefile 头文件查找路径

        

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。

1.三个C文件和三个头文件
    此处的例子稍微复杂些但更接近实际情况。
    文件结果如下:根目录中包含test.c makefileh和文件夹test-add和文件夹test-sub。
test.c makefile
    【test-add】test-add.c test-add.h
    【test-sub】test-sub.c test-sub.h

    【test.c】
  1. #include <stdio.h>  
  2. #include <test-add.h>  
  3. #include <test-sub.h>  
  4. int main(void)  
  5. {  
  6.     int a = 3;  
  7.     int b = 2;  
  8.          
  9.     printf("a=%d\n", a);  
  10.     printf("b=%d\n", b);  
  11.    
  12.     printf("a+b=%d\n", add(a,b));  
  13.     printf("a-b=%d\n", sub(a,b));  
  14.     return 0;  
  15. }  
    【test-add.c】
  1. #include <test-add.h>  
  2. int add(int a, int b)  
  3. {  
  4.     return a+b;  
  5. }  
    【test-add.h】
  1. #ifndef __TEST_ADD  
  2. int add(int a, int b);  
  3. #endif  
    【test-sub.c】
  1. #include "test-sub.h"  
  2. int sub(int a, int b)  
  3. {  
  4.     return a-b;  
  5. }  
    【test-sub.h】
  1. #ifndef __TEST_SUB  
  2. int sub(int a, int b);  
  3. #endif  

2.复习gcc指令
    gcc指令可通过-I前缀指定头文件路径,特别说明./代表当前路径,../代表上一级目录。

3.编写makefile
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 指令编译器和选项  
  2. CC=gcc  
  3. CFLAGS=-Wall -std=gnu99  
  4.   
  5. # 目标文件  
  6. TARGET=test  
  7. SRCS = test.c \  
  8.   ./test-add/test-add.c \  
  9.   ./test-sub/test-sub.c  
  10.   
  11. INC = -I./test-add -I./test-sub  
  12.   
  13. OBJS = $(SRCS:.c=.o)  
  14.   
  15. $(TARGET):$(OBJS)  
  16. #   @echo TARGET:$@  
  17. #   @echo OBJECTS:$^  
  18. [tab]$(CC) -o $@ $^  
  19.   
  20. clean:  
  21. [tab]rm -rf $(TARGET) $(OBJS)  
  22.   
  23. %.o:%.c  
  24. [tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<  
    【具体说明】
    【1】相比于单个文件和多个文件的makefile,通过变量INC制定了头文件路径。头文件路径之间通过空格隔开。
    【2】编译规则%.o:%.c中加入了头文件参数$(CC) $(CFLAGS)  $(INC) -o $@ -c $<,那么在编译的过程中便会出现
    gcc -Wall -std=gnu99  -I./test-add -I./test-sub -o test.o -c test.c。和单个文件和多个文件的makefile相比增加了头文件路径参数。
    【3】SRCS变量中,文件较多时可通过“\”符号续行。

     【编译】
    make clean && make
     【控制台输出】
rm -rf test test.o ./test-add/test-add.o ./test-sub/test-sub.o
gcc -Wall -std=gnu99  -I./test-add -I./test-sub -o test.o -c test.c
gcc -Wall -std=gnu99  -I./test-add -I./test-sub -o test-add/test-add.o -c test-add/test-add.c
gcc -Wall -std=gnu99  -I./test-add -I./test-sub -o test-sub/test-sub.o -c test-sub/test-sub.c
gcc -o test test.o test-add/test-add.o test-sub/test-sub.o

    从控制台的输出可以看出,通过make clean清除上一次的可执行文件和目标文件,然后依次编译各个C文件,在编译的过程中制定了头文件路径,最后把3个目标文件链接为最终可执行文件。

四、例说makefile 增加宏定义

        0.前言

    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。
    【本例说明】
    本例将说明makefile文件中如何加入宏定义。

1.gcc复习
    宏定义使用前缀-D,在编译过程中可以把宏定义追加到CFLAG中。宏定义有两种相似的写法
    【第一种】-D DEFINES 
    【第二种】-D DEFINES=CONDITION

2.源文件
    使用两种不同的方式,通过宏定义包裹打印功能,分别使用#ifdef和#if
  1. #include <stdio.h>  
  2. #include <test-add.h>  
  3. #include <test-sub.h>  
  4. int main(void)  
  5. {  
  6.     int a = 3;  
  7.     int b = 2;  
  8.          
  9.     printf("a=%d\n", a);  
  10.     printf("b=%d\n", b);  
  11. #ifdef TEST_ADD  
  12.     printf("a+b=%d\n", add(a,b));  
  13. #endif  
  14. #if TEST_SUB  
  15.     printf("a-b=%d\n", sub(a,b));  
  16. #endif  
  17.     return 0;  
  18. }  

3.makefile
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 指令编译器和选项  
  2. CC=gcc  
  3. CFLAGS=-Wall -std=gnu99  
  4. # 宏定义  
  5. DEFS = -DTEST_ADD -DTEST_SUB=1  
  6. CFLAGS += $(DEFS)  
  7. # 目标文件  
  8.       
  9. TARGET=test  
  10. # 源文件  
  11. SRCS = test.c \  
  12.   ./test-add/test-add.c \  
  13.   ./test-sub/test-sub.c  
  14. # 头文件查找路径  
  15. INC = -I./test-add -I./test-sub  
  16. # 目标文件  
  17. OBJS = $(SRCS:.c=.o)  
  18. # 链接为可执行文件  
  19. $(TARGET):$(OBJS)  
  20. #   @echo TARGET:$@  
  21. #   @echo OBJECTS:$^  
  22. [tab]$(CC) -o $@ $^  
  23. clean:  
  24. [tab]rm -rf $(TARGET) $(OBJS)  
  25. # 连续动作,请清除再编译链接,最后执行  
  26. exec:clean $(TARGET)  
  27. [tab]@echo 开始执行  
  28. [tab]./$(TARGET)  
  29. [tab]@echo 执行结束  
  30. # 编译规则 $@代表目标文件 $< 代表第一个依赖文件  
  31. %.o:%.c  
  32. [tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<  

4.具体说明
    【1】 makefile定义头文件的方法有两种
    【第一种】-D DEFINES 
    【第二种】-D DEFINES=CONDITION

    【2】DEFS = -DTEST_ADD -DTEST_SUB=1 
    为了说明问题,此处使用了两种不同的写法。此时两处打印功能均被执行

    【3】CFLAGS += $(DEFS) 
    追加到CFLAGS中,此处需要强调CFLAGS只是一个变量,可以命名为任何合法的名称,只要在编译过程中引用该参数即可。
    $(CC)  $(CFLAGS) $(INC) -o $@ -c $< 

5.执行过程
    【编译和链接】
    make clean && make
    【控制台输出】
rm -rf test test.o ./test-add/test-add.o ./test-sub/test-sub.o
gcc -Wall -std=gnu99  -DTEST_ADD -DTEST_SUB=1 -I./test-add -I./test-sub -o test.o -c test.c
gcc -Wall -std=gnu99  -DTEST_ADD -DTEST_SUB=1 -I./test-add -I./test-sub -o test-add/test-add.o -c test-add/test-add.c
gcc -Wall -std=gnu99  -DTEST_ADD -DTEST_SUB=1 -I./test-add -I./test-sub -o test-sub/test-sub.o -c test-sub/test-sub.c
gcc -o test test.o test-add/test-add.o test-sub/test-sub.o
    从控制台的输出可以看出,在编译过程中加入了-D参数。
    【执行】
a=3
b=2
a+b=5
a-b=1
    最终效果和预期完全相同,makefile得到的验证。

6.总结
    【1】增加宏定义的两个方法 -D DEFINES  和 -D DEFINES=CONDITION

    【2】宏定义追加到CFLAG之后

五、例说makefile 增加系统共享库

        

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。
    【本例说明】
    本例将说明如何再makefile文件中增加共享库。

1.gcc复习
    增加共享库使用-l前缀,请注意此处必须要小写。
    【1】一定情况下,系统默认的搜索库目录位于 /usr/lib 和 lib
    【2】若共享库不在系统默认库中,可通过-L参数指定目录。请注意该参数只有在链接过程有效。

2.源文件
    源文件非常简单,打印sin(30)的结果。由于使用math库所以需要引入m.a共享库。
    请注意,sin函数和cos函数使用弧度制;传入的参数为double类型,printf时需要使用lf前缀。
  1. #include <stdio.h>  
  2. #include <math.h>  
  3. #define PI 3.14159265  
  4. int main(void)  
  5. {  
  6.     double angle = 30.00 ;   
  7.     printf("sin(%.2lf)=%.2lf\n", angle, sin(angle * PI / 180));  
  8.     return 0;  
  9. }  

3.makefile
    请替换其中的[tab],并以代码仓库中的makefile文件为主    
  1. # 指令编译器和选项  
  2. CC = gcc  
  3. CFLAGS = -Wall -std=gnu99  
  4.   
  5. # 可执行文件  
  6. TARGET=test  
  7. # C文件  
  8. SRCS = test.c  
  9. # 目标文件  
  10. OBJS = $(SRCS:.c=.o)  
  11.   
  12. # 库文件  
  13. DLIBS = -lm  
  14.   
  15. # 链接为可执行文件  
  16. $(TARGET):$(OBJS)  
  17. #   @echo TARGET:$@  
  18. #   @echo OBJECTS:$^  
  19. [tab]$(CC) -o $@ $^ $(DLIBS)  
  20.   
  21. clean:  
  22. [tab]rm -rf $(TARGET) $(OBJS)  
  23.   
  24. # 连续动作,请清除再编译链接,最后执行  
  25. exec:clean $(TARGET)  
  26. [tab]@echo 开始执行  
  27. [tab]./$(TARGET)  
  28. [tab]@echo 执行结束  
  29.   
  30. # 编译规则 $@代表目标文件 $< 代表第一个依赖文件  
  31. %.o:%.c  
  32. [tab]$(CC) $(CFLAGS) -o $@ -c $<  

4.具体说明
    【1】DLIBS = -lm 增加共享库。请注意实际的库全名为 libm.so
    【2】$(CC) -o $@ $^ $(DLIBS) 编译时增加共享库。请注意,共享库增加在链接过程即可,不需要在编译过程中增加。
    【3】由于libm.so为系统库,所以并不需要指定路径。linux系统中共享库的默认查找路径为/lib和/usr/lib

5.执行过程
    【验证】
    ldd test
linux-vdso.so.1 =>  (0x00007fffde960000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ffe55b18000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffe55750000)
                /lib64/ld-linux-x86-64.so.2 (0x00007ffe55e38000)
    从控制台的输出可以看出,成功链接了系统共享库 libm.so
    【编译和链接】
    make clean && make
    【控制台输出】
rm -rf test test.o
gcc -Wall -std=gnu99 -o test.o -c test.c
gcc -o test test.o  -lm
    从控制台输出可以看出,在链接过程中加入了库文件。
    【执行】
./test
sin(30.00)=0.50
    执行结果正常,符合预期效果。

6.总结
    【1】增加系统共享库时使用前缀-l。
    【2】系统共享库不需要指定路径。
   

六、例说makefile 增加自定义共享库

    

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。
    【本例说明】
    本例分为两个过程,先制作一个共享库,然后在使用这个共享库。共享库采用动态链接方式,后缀为.so。
    
1.gcc复习
    【1】gcc指令中使用共享库使用前缀-l,共享库查找目录使用-L。
    【2】一般情况下DLIBS中加入共享库,而LDFLAGS中加入共享库查找目录。

2.共享库源文件
    和生成共享库有关的文件位于libtest目录中,在makefile最后把共享库复制到同级的lib目录中。具体内容请参考代码仓库并复制zip包。
    【libtest.h】
    指定接口,给出相应声明
  1. #ifndef __LIBTEST_H  
  2. #define __LIBTEST_H  
  3. int sub(int a, int b);  
  4. int add(int a, int b);  
  5. #endif  
    【test-add.c】
  1. int add(int a, int b)  
  2. {  
  3.     return a+b;  
  4. }  
    【test-sub.c】
  1. int sub(int a, int b)  
  2. {  
  3.     return a-b;  
  4. }  

3.共享库的makefile
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 指令编译器和选项  
  2. CC = gcc  
  3. CFLAGS = -Wall -std=gnu99  
  4.   
  5. # 目标文件  
  6. TARGET = libtest.so  
  7. # C文件  
  8. SRCS = test-add.c test-sub.c  
  9. # 目标文件  
  10. OBJS = $(SRCS:.c=.o)  
  11.   
  12. # 链接为可执行文件  
  13. $(TARGET):$(OBJS)  
  14. [tab]$(CC) -shared -o $@ $^  
  15. [tab]mv $(TARGET) ../lib  
  16.   
  17. clean:  
  18. [tab]rm -rf $(TARGET) $(OBJS)  
  19.   
  20. # 编译规则 $@代表目标文件 $< 代表第一个依赖文件  
  21. %.o:%.c  
  22. [tab]$(CC) $(CFLAGS) -o $@ -fPIC -c $<  

    【说明】
    【1】目标为libtest.so,请务必以lib开头 .so结尾,在使用该共享库时系统会自动的 去除lib和.so,使用时写成 -ltest
    【2】多数内容和学习笔记中的多个文件相同。
    【3】在链接过程中加入 -shared参数,意为共享形式的目标文件。
    【4】在编译过程中加入 -fPIC参数,意为生成和地址无关的目标文件。
    【5】在链接完成之后,通过mv指令把libtest.so移动到同级的lib目录中。
    【6】如果有必要,可以把libtest.so复制到/usr/lib目录中,这样使用起来会更加方便。

    【验证】
    ldd test
    linux-vdso.so.1 =>  (0x00007fffde960000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ffe55b18000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffe55750000)
    /lib64/ld-linux-x86-64.so.2 (0x00007ffe55e38000)

    【编译和执行】
    make clean && make 
    【控制台输出】
rm -rf libtest.so test-add.o test-sub.o
gcc -Wall -std=gnu99 -o test-add.o  -fPIC -c test-add.c
gcc -Wall -std=gnu99 -o test-sub.o  -fPIC -c test-sub.c
gcc  -shared -o libtest.so test-add.o test-sub.o
mv libtest.so ../lib
    【1】编译过程中加入了-fPIC选项。
    【2】链接过程中加入-shared选项。
    【3】共享库为libtest.so。
    【4】最后,共享库被移动到上一级的lib目录中。
   
4.源文件
    调用共享库指定的接口,add和sub函数。
  1. #include <stdio.h>  
  2. #include <libtest.h>  
  3. int main(void)  
  4. {  
  5.     int a = 3;  
  6.     int b = 2;  
  7.          
  8.     printf("a=%d\n", a);  
  9.     printf("b=%d\n", b);  
  10.      
  11.     printf("a+b=%d\n", add(a, b));  
  12.     printf("a-b=%d\n", sub(a, b));  
  13.     return 0;  
  14. }  
    
5.makefile
    请替换其中的[tab],并以代码仓库中的makefile文件为主。    
  1. # 指令编译器和选项  
  2. CC = gcc  
  3. CFLAGS = -Wall -std=gnu99  
  4.   
  5. # 目标文件  
  6. TARGET = test  
  7. # C文件  
  8. SRCS = test.c  
  9. # 头文件查找路径  
  10. INC = -I.  
  11. # 库文件和库查找路径  
  12. DLIBS = -ltest  
  13. LDFLAGS = -L./lib  
  14. # 指定运行时的库文件路径  
  15. RPATH = -Wl,-rpath=./lib  
  16.   
  17. # 目标文件  
  18. OBJS = $(SRCS:.c=.o)  
  19.   
  20. # 链接为可执行文件  
  21. $(TARGET):$(OBJS)  
  22. [tab]$(CC) -o $@ $^ $(LDFLAGS) $(DLIBS) $(RPATH)  
  23.   
  24. clean:  
  25. [tab]rm -rf $(TARGET) $(OBJS)  
  26.   
  27. # 连续动作,请清除再编译链接,最后执行  
  28. exec:clean $(TARGET)  
  29. [tab]@echo 开始执行  
  30. [tab]./$(TARGET)  
  31. [tab]@echo 执行结束  
  32.   
  33. # 编译规则 $@代表目标文件 $< 代表第一个依赖文件  
  34. %.o:%.c  
  35. [tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<  

6.具体说明
    【1】DLIBS =  -ltest 指定共享库,请注意共享库的名称为 libtest.so,而-l参数只取test部分,去掉前缀lib和后缀.so。
    【2】LDFLAGS = -L./lib 指定共享库路径,请注意上一步中已经把共享库复制到lib目录中。
    【3】INC = -I./lib  指定libtest.h目录,也可把libtest.h复制到test.c所在的目录。
    【4】$(CC) -o $@ $^ $(LDFLAGS) $(DLIBS) 链接过程指定共享库查找路径,指定共享库名称。
    【5】第【1】和第【2】点只在链接过程有效,在执行过程中需要通过-Wl,-rpath=<path>指定共享库路径。
    
    【编译和链接】
    make clean && make
    【控制台输出】
rm -rf test test.o
gcc -Wall -std=gnu99 -I./lib -o test.o -c test.c 
gcc -o test test.o  -L./lib -ltest -Wl,-rpath=./lib

    【执行】
    在执行之前还可以通过ldd test查看libtest是否链接成功。
./test
a=3
b=2
a+b=5
a-b=1
    从控制台的输出结果可以看出,共享库运行正常。

7.总结
    【1】-l指定共享库名称,-L指定共享库路径。

七、例说makefile 综合案例


        

0.前言
    从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力。所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法。
    例说makefile分为以下几个部分,更多内容请参考【 例说makefile索引博文
    1.只有单个C文件   
    2.含有多个C文件    
    3.需要包括头文件路径
    4.增加宏定义
    5.增加系统共享库
    6.增加自定义共享库
    7.一个实际的例子

    【代码仓库】—— makefile-example
    代码仓库位于bitbucket,可借助TortoiseHg(GUI工具)克隆代码或者在网页中直接下载zip包。
    【本例说明】
    本例借助开源项目freemodbus展开来分析makefile的具体用法,freemodbus包括多个平台的移植分支,本例主要从linuxTCP入手。本文将不会阐述freemodbus的具体用法,而是以makefile的用法为主。

1.makefile文件
    请替换其中的[tab],并以代码仓库中的makefile文件为主。
  1. # 指定编译器  
  2. CC = gcc  
  3.   
  4. # CFLAG包括头文件目录  
  5. CFLAGS = -g -Wall  
  6.   
  7. # 头文件查找路径  
  8. INC = -Iport -I../../modbus/rtu \  
  9.   -I../../modbus/ascii -I../../modbus/include -I../../modbus/tcp  
  10.   
  11. # 静态链接库  
  12. LDFLAGS =   
  13. LDLIBS = -lpthread  
  14. # 目标  
  15. TARGET = tcpmodbus  
  16. # 源文件  
  17. SRC = demo.c port/portother.c \  
  18.  port/portevent.c port/porttcp.c \  
  19.  ../../modbus/mb.c ../../modbus/tcp/mbtcp.c \  
  20.  ../../modbus/functions/mbfunccoils.c \  
  21.  ../../modbus/functions/mbfuncdiag.c \  
  22.  ../../modbus/functions/mbfuncholding.c \  
  23.  ../../modbus/functions/mbfuncinput.c \  
  24.  ../../modbus/functions/mbfuncother.c \  
  25.  ../../modbus/functions/mbfuncdisc.c \  
  26.  ../../modbus/functions/mbutils.c  
  27.   
  28. # 源文件编译为目标文件  
  29. OBJS = $(SRC:.c=.o)  
  30.   
  31. # 链接为可执行文件  
  32. $(TARGET): $(OBJS)  
  33. [tab]$(CC) $^ -o $@ $(LDFLAGS) $(LDLIBS)  
  34.   
  35. # 清除可执行文件和目标文件  
  36. clean:  
  37. [tab]rm -f $(OBJS)  
  38. [tab]rm -f $(TARGET)  
  39.   
  40. # 编译规则 加入头文件 $@代表目标文件 $< 代表第一个依赖文件  
  41. %.o:%.c  
  42. [tab]$(CC) $(CFLAGS) $(INC) -o $@ -c $<  

2.具体说明
    【1】freemodbus中的文件较多,INC包括所有的头文件查找路径,路径使用前缀-I表示,请注意若路径较多时通过"\"实现续行。
    【2】此处使用了共享库pthread,共享库的设备一般包括两个方面。第一,共享库的名称 DLIBS,共享库的查找路径LDFLAGS。由于pthread为系统共享库,使用了系统默认的查找路径 /usr/lib,所以LDFLAGS设置可以省略,若需要制定共享库目录,在目录前加入-L即可。
    【3】$(CC) $^ -o $@  $(LDFLAGS) $(LDLIBS) 和共享库设置有关的参数需要放在链接指令中。请注意共享库应该插入链接过程中而不是编译过程。

3.执行过程
    make clean && make
    【控制台输出】
    输出内容较多,此处省略。

    【运行】
    sudo ./tcpmodbus
[plain] view plain copy
  1. Type 'q' for quit or 'h' for help!  
  2. > h  
  3. FreeModbus demo application help:  
  4.   'd' ... disable protocol stack.  
  5.   'e' ... enabled the protocol stack  
  6.   's' ... show current status  
  7.   'q' ... quit applicationr  
  8.   'h' ... this information  
  9. >   

    【说明】
    【1】编译时加入了头文件查找路径。
    【2】链接时加入了共享库。

4.总结
    【1】LDIBS指定共享库,共享库以 -l 为前缀。
    【2】LDFLAGS指定共享库查找路径,路径以 -L 为前缀。




本文章转载于https://blog.csdn.net/xukai871105/article/details/37083675







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值